引入orderbase,规范api接口模块

This commit is contained in:
cyonjan 2025-07-05 21:43:01 +08:00
parent 64f2dd29f6
commit 78b91faa55
19 changed files with 3825 additions and 70 deletions

106
package-lock.json generated
View File

@ -15,7 +15,8 @@
"pinia": "^3.0.2",
"pinia-plugin-persistedstate": "^4.2.0",
"vue": "^3.5.13",
"vue-router": "^4.5.1"
"vue-router": "^4.5.1",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.3",
@ -2165,6 +2166,15 @@
"node": ">=0.4.0"
}
},
"node_modules/adler-32": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz",
"integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/async-validator": {
"version": "4.2.5",
"resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
@ -2321,6 +2331,19 @@
],
"license": "CC-BY-4.0"
},
"node_modules/cfb": {
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz",
"integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
"license": "Apache-2.0",
"dependencies": {
"adler-32": "~1.3.0",
"crc-32": "~1.2.0"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz",
@ -2345,6 +2368,15 @@
"consola": "^3.2.3"
}
},
"node_modules/codepage": {
"version": "1.15.0",
"resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz",
"integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
@ -2406,6 +2438,18 @@
"url": "https://opencollective.com/core-js"
}
},
"node_modules/crc-32": {
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz",
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
"license": "Apache-2.0",
"bin": {
"crc32": "bin/crc32.njs"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
@ -2887,6 +2931,15 @@
"node": ">= 6"
}
},
"node_modules/frac": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz",
"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/fs-extra": {
"version": "11.3.0",
"resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.3.0.tgz",
@ -4070,6 +4123,18 @@
"node": ">=0.10.0"
}
},
"node_modules/ssf": {
"version": "0.11.2",
"resolved": "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz",
"integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
"license": "Apache-2.0",
"dependencies": {
"frac": "~1.1.2"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/std-env": {
"version": "3.9.0",
"resolved": "https://registry.npmmirror.com/std-env/-/std-env-3.9.0.tgz",
@ -4589,6 +4654,45 @@
"node": ">= 12"
}
},
"node_modules/wmf": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz",
"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/word": {
"version": "0.3.0",
"resolved": "https://registry.npmmirror.com/word/-/word-0.3.0.tgz",
"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/xlsx": {
"version": "0.18.5",
"resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz",
"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
"license": "Apache-2.0",
"dependencies": {
"adler-32": "~1.3.0",
"cfb": "~1.2.1",
"codepage": "~1.15.0",
"crc-32": "~1.2.1",
"ssf": "~0.11.2",
"wmf": "~1.0.1",
"word": "~0.3.0"
},
"bin": {
"xlsx": "bin/xlsx.njs"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz",

View File

@ -16,7 +16,8 @@
"pinia": "^3.0.2",
"pinia-plugin-persistedstate": "^4.2.0",
"vue": "^3.5.13",
"vue-router": "^4.5.1"
"vue-router": "^4.5.1",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.3",

View File

@ -30,3 +30,96 @@ export const supplierList = () => {
url: `/member/supplierlist/`,
})
}
export const searchMember = (keyword) => {
return service({
method: 'post',
url: '/member/matchmember/' + keyword
})
}
export const memberInfo = (id) => {
return service({
method: 'post',
url: '/member/memberinfo/' + id
})
}
export const searchBrand = (keyword) => {
return service({
method: 'post',
url: '/accessory/searchbrand/' + keyword
})
}
export const searchProduct = (code) => {
return service({
method: 'post',
url: '/product/searchproductbycode/' + code
})
}
export const partsGoodsById = (id) => {
return service({
method: 'get',
url: '/product/goodsById?id=' + id,
})
}
export const hasProduct = (brand,code) => {
return service({
method: 'post',
url: '/product/hasproduct',
data: {
code: code,
brand: brand
}
})
}
export const partsGoodsByCode = (code,brand=0) => {
return service({
method: 'get',
url: '/product/goodsByCode?code=' + code + (brand ? '&brand=' + brand : ''),
})
}
export const onlyGoodsByCode = (code,brand=0) => {
return service({
method: 'get',
url: '/product/onlyGoodsByCode?code=' + code + (brand ? '&brand=' + brand : ''),
})
}
export const productInfo = (id) => {
return service({
method: 'post',
url: '/product/product?id=' + id
})
}
export const hasFocus = (brand,code) => {
return service({
method: 'get',
url: `/store/hasfocus?brand=${brand}&code=${code}`,
})
}
export const focusItem = (id) => {
return service({
method: 'get',
url: '/store/focus_item/' + id,
})
}
export const updateFocus = (data) => {
return service({
method: 'post',
url: '/store/updatefocus',
data
})
}
export const addFocusLog = (id,user_id,note) => {
return service({
method: 'post',
url: '/store/addfocuslog?id=' + id,
data: {
note,
operator: user_id,
}
})
}

View File

@ -12,7 +12,6 @@ export const sellerList = () => {
url: '/accessory/sellerlist/'
})
}
export const inquiryFilter = (users,status,days=7) => {
return service({
method: 'get',
@ -24,7 +23,6 @@ export const inquiryFilter = (users,status,days=7) => {
}
})
}
export const inquiryList = (users,keys={},page=1,page_size=20) => {
return service({
method: 'post',
@ -37,14 +35,12 @@ export const inquiryList = (users,keys={},page=1,page_size=20) => {
}
})
}
export const customerList = () => {
return service({
method: 'get',
url: '/store/customerList'
})
}
export const inquiryStatus = (ids) => {
return service({
method: 'post',
@ -54,18 +50,24 @@ export const inquiryStatus = (ids) => {
}
})
}
export const createInquiry = (user_id,note='') => {
export const createInquiry = (user_id,note='',subject='') => {
return service({
method: 'post',
url: '/store/createInquiry',
data: {
user_id,
note
note,
subject
}
})
}
export const update = (id,data) => {
return service({
method: 'post',
url: '/store/update/' + id,
data
})
}
export const removeInquiry = (ids) => {
return service({
method: 'post',
@ -75,7 +77,6 @@ export const removeInquiry = (ids) => {
}
})
}
export const addInquiryGoods = (user_id,goods) => {
return service({
method: 'post',
@ -86,3 +87,23 @@ export const addInquiryGoods = (user_id,goods) => {
}
})
}
export const inquiryInfo = (id) => {
return service({
method: 'get',
url: '/store/info',
params: {
id
}
})
}
export const inquiryGoodsList = (id) => {
return service({
method: 'get',
url: '/store/goodsList',
params: {
id
}
})
}

107
src/api/product.js Normal file
View File

@ -0,0 +1,107 @@
// 产品商品类API
import service from '@/service'
export const searchProduct = (code) => {
return service({
method: 'post',
url: '/product/searchproductbycode/' + code
})
}
export const partsGoodsById = (id) => {
return service({
method: 'get',
url: '/product/goodsById',
params: {
id
}
})
}
export const hasProduct = (brand,code) => {
return service({
method: 'post',
url: '/product/hasproduct',
data: {
code: code,
brand: brand
}
})
}
export const hasFocusBatch = (data) => {
return service({
method: 'post',
url: '/store/hasfocusbatch',
data
})
}
export const hasBrandProduct = (brand,code) => {
return service({
method: 'post',
url: '/product/hasbrandproduct',
data: {
code: code,
brand: brand
}
})
}
export const partsGoodsByCode = (code,brand=0) => {
return service({
method: 'get',
url: '/product/goodsByCode',
params: {
code,
brand
}
})
}
export const onlyGoodsByCode = (code,brand=0) => {
return service({
method: 'get',
url: '/product/onlyGoodsByCode?code=' + code + (brand ? '&brand=' + brand : ''),
})
}
export const productInfo = (id) => {
return service({
method: 'post',
url: '/product/product?id=' + id
})
}
export const hasFocus = (brand,code) => {
return service({
method: 'get',
url: `/store/hasfocus?brand=${brand}&code=${code}`,
})
}
export const focusItem = (id) => {
return service({
method: 'get',
url: '/store/focus_item/' + id,
})
}
export const updateFocus = (data) => {
return service({
method: 'post',
url: '/store/updatefocus',
data
})
}
export const addFocusLog = (id,user_id,note) => {
return service({
method: 'post',
url: '/store/addfocuslog?id=' + id,
data: {
note,
operator: user_id,
}
})
}

View File

@ -40,6 +40,29 @@ export const getEmailCode = (email) => {
export const loadShopInfo = () => {
return service({
method: 'get',
url: '/store/loadShopInfo'
url: '/store/loadShopInfo',
})
}
export const addLog = (order_id,user,action,note) => {
return service({
method: 'post',
url: '/store/addLog',
data: {
order_id,
action,
user,
note
}
})
}
export const getLog = (id) => {
return service({
method: 'get',
url: '/store/getLog',
params: {
order_id: id,
}
})
}

View File

@ -0,0 +1,76 @@
<template>
<el-autocomplete
v-model="brand_name"
:fetch-suggestions="search"
@select="handleSelect"
clearable
class="inline-input w-50"
placeholder="品牌关键字"
:style="{width: props.width + 'px','font-size': props.font + 'px'}"
/>
</template>
<script setup>
import { ref, watch } from 'vue'
import { searchBrand } from '@/api'
const props = defineProps({
modelValue: {
type: Object,
default: () => ({ id: 0, name: '' })
},
width: {
type: Number,
default: 140
},
font: {
type: Number,
default: 12
}
})
const emits = defineEmits(['update:modelValue'])
const brand_name = ref(props.modelValue.name)
watch(() => props.modelValue,
(newValue) => {
brand_name.value = newValue.name
},{ deep: true }
)
const search = async (qs, cb) => {
if(qs && qs.length >= 2) {
try {
const {data:slist} = await searchBrand(qs)
var list = slist.data
if (list.length == 0) {
list.push({
id: '-1',
value: '无匹配结果'
})
cb(list);
} else {
list = list.map(item => ({
value:`${item.name} ${item.name_en}`,
id: `${item.id}`,
name: item.name ? item.name : item.name_en
})
)
cb(list);
}
} catch (error) {
console.log(error)
}
}
}
const handleSelect = (item) => {
if(item.id != -1) {
let selectedBrand = {
id: item.id,
name: item.name,
}
emits('update:modelValue',selectedBrand)
} else {
emits('update:modelValue',{ id: 0, name: brand_name.value })
}
}
</script>

832
src/components/Inquiry.vue Normal file
View File

@ -0,0 +1,832 @@
<template>
<h2>{{ order_id,orderStatus }}</h2>
<!--
<div>
<el-button size="large" type="primary" @click="makeInquiry" :icon="Message">
添加询价单
</el-button>
<el-button size="large" type="success" @click="refresh" :icon="RefreshRight">
刷新列表
</el-button>
</div>
<div>
<el-table
ref="inquiryTableRef"
fit
:data="inquiryList"
class="w-full mt-4"
row-class-name="no-expand-icon"
row-key="id"
@row-click="expandPanel"
@expand-change="expandChange"
>
<el-table-column type="expand" width="0">
<template #default="{row}">
<div class="flex justify-center items-center mt-2">
<b>{{ row.supplier_name }}</b>
</div>
<div class="flex justify-center mt-2">
<el-table
size="small"
fit
:data="row.return_data"
class="text-xs mt-4 w-auto"
style="border: 1px solid black;"
:header-cell-style="{background: '#e0f2fe', color: 'black',borderColor: 'black'}"
:cell-style="{borderColor: 'black'}"
border
>
<el-table-column type="index" label="#" width="50" align="center" header-align="center"/>
<el-table-column prop="code" label="件号" show-overflow-tooltip width="120" align="center" header-align="center"/>
<el-table-column prop="new_code" label="更新件号" show-overflow-tooltip width="120" align="center" header-align="center"/>
<el-table-column prop="b_name" label="品牌" show-overflow-tooltip width="100" align="center" header-align="center"/>
<el-table-column prop="market" label="面价" show-overflow-tooltip width="100" align="right" header-align="center">
<template #default="{row}">
{{ (Math.round(row.market * 100) / 100.00).toFixed(2) }}
</template>
</el-table-column>
<el-table-column prop="price" label="报价" show-overflow-tooltip width="100" align="right" header-align="center">
<template #default="{row}">
<?-- <div :style="row.market!=0 && row.price>(row.market*0.5) ? {'color': 'black'} : {'color': 'red'}">
{{ (Math.round(row.price * 100) / 100.00).toFixed(2) }}
</div> --?>
<div :class="row.market!=0 && row.price>(row.market*0.5) ? 'text-black' : 'text-red font-bold'">
{{ (Math.round(row.price * 100) / 100.00).toFixed(2) }}
</div>
<?-- {{ (Math.round(row.price * 100) / 100.00).toFixed(2) }} --?>
</template>
</el-table-column>
<el-table-column prop="amount" label="数量" width="60" align="right" header-align="center">
<template #default="{row}">
{{ row.amount }}
</template>
</el-table-column>
<?-- <el-table-column label="总价" show-overflow-tooltip width="120" align="right" header-align="center">
<template #default="{row}">
<span style="color: darkblue;font-weight: bold;">{{ (Math.round(row.price * row.amount * 100) / 100.00).toFixed(2) }}</span>
</template>
</el-table-column> --?>
<el-table-column prop="nums" label="库存" width="60" align="right" header-align="center"/>
<el-table-column prop="weight" label="重量" width="80" align="right" header-align="center">
<template #default="{row}">
{{ (Math.round(row.weight * 1000) / 1000.00).toFixed(3) }}
</template>
</el-table-column>
<el-table-column prop="period" label="货期" width="60" align="right" header-align="center"/>
<el-table-column prop="source" label="货源" show-overflow-tooltip width="80" header-align="center"/>
<el-table-column prop="note" label="备注" show-overflow-tooltip width="80" header-align="center"/>
</el-table>
</div>
</template>
</el-table-column>
<el-table-column prop="order_no" label="订单号" width="140" header-align="center" align="center" show-overflow-tooltip>
<template #default="{row}">
<el-text size="small">{{ row.order_no }}</el-text>
</template>
</el-table-column>
<el-table-column prop="supplier_name" label="供应商" width="120" header-align="center" align="center" show-overflow-tooltip>
<template #default="{row}">
<el-text size="small">{{ row.supplier_name }}</el-text>
</template>
</el-table-column>
<el-table-column label="客户" width="200" show-overflow-tooltip header-align="center" align="center">
<template #default="{row}">
<el-text size="small">{{ row.true_name ? row.true_name : row.user_name}}</el-text>
</template>
</el-table-column>
<el-table-column label="询价品类" header-align="center" width="120px" align="center">
<template #default="{row}">
<el-popover effect="light" trigger="hover" placement="left-start" width="auto">
<template #default>
<el-table
fit
:data="row.products"
:height="240"
style="width:400px"
>
<el-table-column prop="code" label="编码" :show-overflow-tooltip="true" width="100px"/>
<el-table-column prop="b_name" label="品牌" :show-overflow-tooltip="true" width="80px" />
<el-table-column prop="name" label="品名" :show-overflow-tooltip="true" width="80px" />
<el-table-column prop="amount" label="数量" width="60px" />
<el-table-column prop="note" label="备注" :show-overflow-tooltip="true" width="80px"/>
</el-table>
</template>
<template #reference>
<el-tag type="danger">{{row.products.length}}</el-tag>
</template>
</el-popover>
</template>
</el-table-column>
<el-table-column prop="status" label="询价状态" width="120px" head-align="center" align="center">
<template #default="{row}">
<el-text v-if="row.status==0" style="font-weight: 600;">待询价</el-text>
<el-text v-else-if="row.status==1" type="primary" style="font-weight: 600;">已发函</el-text>
<el-text v-else-if="row.status==2" type="success" style="font-weight: 600;">已报价</el-text>
<el-text v-else type="warning" style="font-weight: 600;">未知状态</el-text>
</template>
</el-table-column>
<el-table-column prop="create_time" label="创建时间" header-align="center" align="center" width="160">
<template #default="{row}">
<el-text size="small">{{ row.create_time }}</el-text>
</template>
</el-table-column>
<el-table-column prop="return_time" label="回函时间" header-align="center" align="center" width="160">
<template #default="{row}">
<el-text size="small">{{ row.return_time }}</el-text>
</template>
</el-table-column>
<el-table-column prop="note" label="询单需求" header-align="center" width="200px" :show-overflow-tooltip="true" />
<el-table-column prop="link" label="链接" header-align="center" align="center">
<template #default="scope">
<el-button size="small" type="success" :icon="ChatDotRound" @click.stop="copyToClipboard(`${scope.row.order_no.substr(2,2)}年${scope.row.order_no.substr(4,2)}月${scope.row.order_no.substr(6,2)}日\n${scope.row.subject}\n${scope.row.link}`)" :disabled="scope.row.status<1">复制报文</el-button>
<el-button size="small" type="info" :icon="Link" @click.stop="copyToClipboard(scope.row.link)" :disabled="scope.row.status<1">复制链接</el-button>
<el-button size="small" type="primary" :icon="Download" @click.stop="downloadExcel(scope.row)">详表</el-button>
<el-button size="small" type="default" :icon="Download" @click.stop="simpleExcel(scope.row)">简表</el-button>
</template>
</el-table-column>
<el-table-column label="操作" header-align="center" align="center">
<template #default="scope">
<el-button
size="small"
type="danger"
:icon="Promotion"
:disabled="parseInt(scope.row.status)!=0"
@click.stop="handleSendLink(scope.$index,scope.row.link)"
/>
<el-button
size="small"
type="primary"
:icon="Upload"
@click.stop="uploadExcel(scope.row)"
:disabled="parseInt(scope.row.status)>=2"
/>
<el-button
size="small"
:type="expandRowKeys.findIndex(it => it == scope.row.id) > -1 ? 'default' : 'success'"
:icon="expandRowKeys.findIndex(it => it == scope.row.id) > -1 ? Hide : View"
:disabled="!scope.row.return_data || scope.row.return_data.length == 0"
@click.stop="expandPanel(scope.row)"
/>
<el-button
size="small"
type="primary"
:icon="Download"
:disabled="!scope.row.return_data || scope.row.return_data.length == 0"
@click.stop="exportReturn(scope.row)"
/>
</template>
</el-table-column>
</el-table>
</div>
<el-dialog
v-model="centerDialogVisible"
title="创建询价函"
width="30%"
destroy-on-close
center
>
<el-card class="box-card-edit">
<el-row>
<el-col :span="12">
<el-table
ref="goodsTableRef"
fit
:data="goodsList"
style="height: 300px;"
@selection-change="handleGoodsSelect"
>
<el-table-column fixed type="selection" width="50px" />
<el-table-column prop="code" label="产品编码" width="160px" align="center">
<template #default="scope">
<el-popover effect="light" trigger="hover" placement="left-start" width="auto">
<template #default>
<div>代码: {{ scope.row.code }}</div>
<div>品名: {{ scope.row.name }}</div>
<div>品牌: {{ scope.row.b_name }}</div>
<div>数量: {{ scope.row.amount }}</div>
<div>备注: {{ scope.row.note }}</div>
</template>
<template #reference>
<el-text v-if="scope.row.new_code" type="primary">{{ scope.row.new_code }}</el-text>
<el-text v-else>{{ scope.row.code }}</el-text>
</template>
</el-popover>
</template>
</el-table-column>
<el-table-column prop="b_name" label="品牌" width="120px" />
</el-table>
</el-col>
<el-col :span="12">
<el-form :inline="true">
<el-form-item label="需求备注">
<el-input v-model="new_inquiry.note" placeholder="需求备注" />
</el-form-item>
<el-form-item label="供应商:">
<el-autocomplete
v-model="supplier_id"
:fetch-suggestions="supplierSearch"
@select="handleSupplierSelect"
clearable
class="inline-input w-50"
placeholder="供应商关键字"
/>
</el-form-item>
</el-form>
<el-table
fit
:data="new_inquiry.supplier_array"
:show-header="false"
empty-text="未选择供应商"
style="height: 198px;"
>
<el-table-column prop="name" label="供应商" width="240px" :show-overflow-tooltip="true"></el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
size="small"
type="danger"
:icon="CloseBold"
@click="removeSupplier(scope.$index)"
/>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
</el-card>
<template #footer>
<span class="dialog-footer">
<el-button type="success" @click="create_inquiry" :icon="Message" :disabled="goods_nums==0 || new_inquiry.supplier_array.length==0">
确认添加
</el-button>
</span>
</template>
</el-dialog>
<el-dialog
v-model="contactDialog"
title="选择供应商联系人"
width="30%"
destroy-on-close
center
>
<el-card class="box-card-edit">
<el-form label-width="100px">
<el-form-item label="联系人">
<el-select v-model="contact_id" placeholder="选择供应商联系人" size="large">
<el-option
v-for="item in contact_list"
:key="item.id"
:label="item.username"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
</el-form>
</el-card>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="selectContact" :icon="Avatar" :disabled="!contact_id">
确认选中
</el-button>
</span>
</template>
</el-dialog>
-->
</template>
<script setup>
import { ref,reactive,computed,onMounted,onUnmounted } from 'vue'
const emit = defineEmits(['next-step','add-goods','make-order'])
import { useUserStore } from '@/store'
const user_store = useUserStore()
const props = defineProps({
'orderId': {
type: Number,
default: 0
},
'orderStatus':{
type: Number,
default: 0
}
})
const orderStatus = ref(0)
const order_id = ref(0)
onMounted(async () => {
order_id.value = +props.orderId
orderStatus.value = +props.orderStatus
console.log('Inquiry mounted',order_id.value,orderStatus.value)
})
onUnmounted(() => {
console.log('Inquiry unmounted')
})
/*
import { $orderGoods,$orderInfo,$orderInquiry,$enquiryReturn,$createInquiry,$sendLink,$actionLog,brandList,supplierList,searchSupplier,getSupplierContact } from '@/api'
import { Message,DocumentCopy,View,RefreshRight,Download,Upload,CloseBold,Avatar,Promotion,ChatDotRound,Link,Hide } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import XLSX from 'xlsx-js-style'
import { md5 } from '@/utils'
const inquiryTableRef = ref()
const goodsList = ref([])
const inquiryList = ref([])
onMounted(async () => {
if(props.orderId) {
order_id.value = props.orderId
const {data:res} = await $orderGoods(order_id.value)
goodsList.value = res.data
const {data:inquiry} = await $orderInquiry(order_id.value)
inquiryList.value = inquiry.data
}
})
const load = async () => {
const {data:bres} = await brandList()
brand_list.value = bres.data
}
load()
const brand_list = ref([])
const goodsTableRef = ref()
const centerDialogVisible = ref(false)
const goodsSelected = ref([])
const goods_nums = computed(() => goodsSelected.length)
const handleGoodsSelect = (val) => goodsSelected.value = val
const expandPanel = (row) => {
if(row.return_data && row.return_data.length > 0) {
inquiryTableRef.value.toggleRowExpansion(row)
}
}
const expandRowKeys = ref([])
const isExpanded = computed(() => (id) => expandRowKeys.value.findIndex(it => it == id))
const expandChange = async (row,rows) => {
expandRowKeys.value = rows.map(item => item.id)
}
const supplier_id = ref('')
const supplierSearch = async (qs, cb) => {
if(qs && qs.length) {
try {
const {data:slist} = await searchSupplier(qs)
var list = slist.data
if (list.length == 0) {
list.push({
id: '-1',
value: '无匹配结果'
})
cb(list);
} else {
list = list.map(item => {
return {
value: item.sname,
id: `${item.sid}`,
name: item.sname,
cname: item.company_name
}
})
cb(list);
}
} catch (error) {
console.log(error)
}
} else {
cb([{id: '-1',value: '无匹配结果'}])
}
}
const handleSupplierSelect = async (item) => {
supplier_id.value = ''
if(item.id != -1 && !new_inquiry.supplier_array.map(m => m.id).includes(item.id)) {
new_inquiry.supplier_array.push({id: item.id,name: item.name,cname: item.cname})
}
}
let new_inquiry = reactive({
supplier_array: [],
note: ''
})
const makeInquiry = () => {
new_inquiry = reactive({
supplier_array: [],
note: ''
})
supplier_id.value = ''
centerDialogVisible.value = true
}
const removeSupplier = (index) => {
new_inquiry.supplier_array.splice(index, 1)
}
const create_inquiry = async () => {
centerDialogVisible.value = false
const {data:order} = await $orderInfo(props.orderId)
let data = {
products: goodsSelected.value,
suppliers: new_inquiry.supplier_array.map(item => item.id),
order_no: order.data.order_no,
user_id: order.data.user_id,
note: new_inquiry.note
}
const {data:res} = await $createInquiry(props.orderId,data)
if(res.data.length) {
await $actionLog(props.orderId,user_store.name,'创建询价函',JSON.stringify(data))
ElMessage({
message: "询价函成功创建!",
type: 'success',
})
const {data:inquiry} = await $orderInquiry(props.orderId)
inquiryList.value = inquiry.data
}
}
const copyToClipboard = (content) => {
if (window.clipboardData) {
window.clipboardData.setData('text', content);
} else {
(function (content) {
document.oncopy = function (e) {
e.clipboardData.setData('text', content);
e.preventDefault();
document.oncopy = null;
}
})(content);
document.execCommand('Copy');
}
}
const contact_list = ref([])
const contact_id = ref(0)
const contactDialog = ref(false)
const inquiryIndex = ref(0)
const inquiryUrl = ref('')
const handleSendLink = async (index,url) => {
contact_id.value = 0
let {data: res} = await getSupplierContact(inquiryList.value[index].supplier_id)
if(res.data.contact.length > 1) {
contact_list.value = res.data.contact
contact_id.value = null
inquiryIndex.value = index
inquiryUrl.value = url
contactDialog.value = true
} else {
if(res.data.contact.length == 1) {
contact_id.value = res.data.contact[0].id
}
copyToClipboard(url)
await $actionLog(props.orderId,user_store.name,'发送询价函','询价函id=' + inquiryList.value[index].id)
if(inquiryList.value[index].status != 2) {
await $sendLink(inquiryList.value[index].id,contact_id.value)
inquiryList.value[index].status = 1
}
ElMessage({
message: "询价函链接已生成!",
type: 'success',
})
}
}
const selectContact = async () => {
contactDialog.value = false
copyToClipboard(inquiryUrl.value)
await $actionLog(props.orderId,user_store.name,'发送询价函','询价函id=' + inquiryList.value[inquiryIndex.value].id)
if(inquiryList.value[inquiryIndex.value].status != 2) {
await $sendLink(inquiryList.value[inquiryIndex.value].id,contact_id.value)
inquiryList.value[inquiryIndex.value].status = 1
}
ElMessage({
message: "询价函链接已生成!",
type: 'success',
})
}
const refresh = async () => {
const {data:inquiry} = await $orderInquiry(props.orderId)
inquiryList.value = inquiry.data
}
const exportReturn = (row) => {
// console.log(row)
const worksheet = XLSX.utils.aoa_to_sheet([[`${row.supplier_name}报价函`]], {origin: 'A1'})
if(!worksheet["!rows"]) {
worksheet["!rows"] = []
}
if(!worksheet["!cols"]) {
worksheet["!cols"] = []
}
if(!worksheet["!rows"][0]) {
worksheet["!rows"][0] = {hpx: 22}
}
worksheet["!cols"][3] = {wch: 20}
worksheet['A1']['s'] = {font:{bold:true,sz:14},alignment:{horizontal:'center',vertical:'center',wrapText:true}}
worksheet['!merges'] = [{s:{r:0,c:0},e:{r:0,c:9}}]
worksheet['!merges'].push({s:{r:1,c:0},e:{r:1,c:3}})
worksheet['!merges'].push({s:{r:1,c:5},e:{r:1,c:9}})
worksheet['F2'] = {t:'d',v: row.return_time,z:'报价日期YYYY年MM月DD日'}
XLSX.utils.sheet_add_aoa(worksheet, [['品牌', '产品件号', '更新件号','品名','购买数','面价单价(元)', '执行单价(元)','紧急单价(元)','库存数','备注']], {origin: "A3"})
let data = row.return_data
const dataRow = 4
const align_center = {
horizontal: 'center',
vertical: 'center',
wrapText: true
}
const align_right = {
horizontal: 'right',
vertical: 'center',
wrapText: true
}
const title_style = {
font: { bold: true },
alignment: align_center
};
const style_center = {
alignment: align_center
}
const style_right = {
alignment: align_right
}
worksheet['A3']['s'] = title_style
worksheet['B3']['s'] = title_style
worksheet['C3']['s'] = title_style
worksheet['D3']['s'] = title_style
worksheet['E3']['s'] = title_style
worksheet['F3']['s'] = title_style
worksheet['G3']['s'] = title_style
worksheet['H3']['s'] = title_style
worksheet['I3']['s'] = title_style
worksheet['J3']['s'] = title_style
data.forEach((v,row) => {
// new_code
// XLSX.utils.sheet_add_aoa(worksheet, [[v.b_name,v.new_code ? v.new_code : v.code,v.new_code ?? '',v.name,v.amount,v.market,v.price,v.price2,v.nums,v.note]],{origin: `A${row + dataRow}`})
XLSX.utils.sheet_add_aoa(worksheet, [[v.b_name,v.code,v.new_code ?? '',v.name,v.amount,v.market,v.price,v.price2,v.nums,v.note]],{origin: `A${row + dataRow}`})
if(worksheet[`A${row + dataRow}`]) {
worksheet[`A${row + dataRow}`]['s'] = style_center
}
if(worksheet[`E${row + dataRow}`]) {
worksheet[`E${row + dataRow}`]['s'] = style_right
worksheet[`E${row + dataRow}`]['s'] = '0'
}
})
const workbook = XLSX.utils.book_new();
if(!workbook.Custprops) {
workbook.Custprops = {}
}
let token = `${row.supplier_id}ji${row.id}fu${row.user_id}jf${row.order_id}`
workbook.Custprops["type"] = 'full';
workbook.Custprops["inquery"] = row.id;
workbook.Custprops["supplier"] = row.supplier_id;
workbook.Custprops["link"] = row.link;
workbook.Custprops["order_no"] = row.order_no;
workbook.Custprops["token"] = md5(token);
XLSX.utils.book_append_sheet(workbook, worksheet, `${row.supplier_name}报价函`);
var time = new Date();
var ext = `${row.supplier_name}-${time.getFullYear()}${time.getMonth() + 1}${time.getDate()}-${Math.round(Math.random() * 10000)}`;
let fn = `${ext}.xlsx`
XLSX.writeFile(workbook,fn,{compression: true});
ElMessage({
message: "询价回函已下载!",
type: 'success',
})
}
const downloadExcel = async (row_data) => {
const {data: order} = await $orderInfo(row_data.order_id)
let seller_name = order.data.seller_name
const worksheet = XLSX.utils.aoa_to_sheet([[`${seller_name}询价单`]], {origin: 'A1'})
if(!worksheet["!rows"]) {
worksheet["!rows"] = []
}
if(!worksheet["!cols"]) {
worksheet["!cols"] = []
}
if(!worksheet["!rows"][0]) {
worksheet["!rows"][0] = {hpx: 22}
}
worksheet["!cols"][3] = {wch: 20}
worksheet['A1']['s'] = {font:{bold:true,sz:14},alignment:{horizontal:'center',vertical:'center',wrapText:true}}
worksheet['!merges'] = [{s:{r:0,c:0},e:{r:0,c:9}}]
worksheet['!merges'].push({s:{r:1,c:0},e:{r:1,c:3}})
worksheet['!merges'].push({s:{r:1,c:5},e:{r:1,c:9}})
worksheet['A2'] = {t:'s',v:`报价单位:${row_data.supplier_name}`}
worksheet['F2'] = {t:'d',v: row_data.create_time,z:'询价日期YYYY年MM月DD日'}
XLSX.utils.sheet_add_aoa(worksheet, [['品牌', '产品件号', '更新件号','品名','购买数','面价单价(元)', '执行单价(元)','紧急单价(元)','库存数','备注']], {origin: "A3"})
let data = row_data.products
const dataRow = 4
const align_center = {
horizontal: 'center',
vertical: 'center',
wrapText: true
}
const align_right = {
horizontal: 'right',
vertical: 'center',
wrapText: true
}
const title_style = {
font: { bold: true },
alignment: align_center
};
const style_center = {
alignment: align_center
}
const style_right = {
alignment: align_right
}
worksheet['A3']['s'] = title_style
worksheet['B3']['s'] = title_style
worksheet['C3']['s'] = title_style
worksheet['D3']['s'] = title_style
worksheet['E3']['s'] = title_style
worksheet['F3']['s'] = title_style
worksheet['G3']['s'] = title_style
worksheet['H3']['s'] = title_style
worksheet['I3']['s'] = title_style
worksheet['J3']['s'] = title_style
data.forEach((v,row) => {
// new_code
// XLSX.utils.sheet_add_aoa(worksheet, [[v.b_name,v.code,'',v.name,v.amount]],{origin: `A${row + dataRow}`})
XLSX.utils.sheet_add_aoa(worksheet, [[v.b_name,v.new_code ? v.new_code : v.code,'',v.name,v.amount]],{origin: `A${row + dataRow}`})
if(worksheet[`A${row + dataRow}`]) {
worksheet[`A${row + dataRow}`]['s'] = style_center
}
if(worksheet[`E${row + dataRow}`]) {
worksheet[`E${row + dataRow}`]['s'] = style_right
worksheet[`E${row + dataRow}`]['s'] = '0'
}
})
const workbook = XLSX.utils.book_new();
if(!workbook.Custprops) {
workbook.Custprops = {}
}
let token = `${row_data.supplier_id}ji${row_data.id}fu${row_data.user_id}jf${row_data.order_id}`
workbook.Custprops["type"] = 'full';
workbook.Custprops["inquery"] = row_data.id;
workbook.Custprops["supplier"] = row_data.supplier_id;
workbook.Custprops["link"] = row_data.link;
workbook.Custprops["order_no"] = row_data.order_no;
workbook.Custprops["token"] = md5(token);
XLSX.utils.book_append_sheet(workbook, worksheet, `${seller_name}询价函`);
var time = new Date();
var ext = `${row_data.supplier_name}-${time.getFullYear()}${time.getMonth() + 1}${time.getDate()}-${Math.round(Math.random() * 10000)}`;
let fn = `${ext}.xlsx`
XLSX.writeFile(workbook,fn,{compression: true});
await $actionLog(props.orderId,user_store.name,'下载询价函','询价函id=' + row_data.id)
if(row_data.status != 2) {
await $sendLink(row_data.id)
row_data.status = 1
}
ElMessage({
message: "询价函已下载!",
type: 'success',
})
}
//
const simpleExcel = async (row_data) => {
const {data: order} = await $orderInfo(row_data.order_id)
let seller_name = order.data.seller_name
let data = row_data.products
if(data.length) {
let sheet_data = []
data.forEach(item => sheet_data.push([item.code,item.amount]))
const worksheet = XLSX.utils.aoa_to_sheet(sheet_data, {origin: 'A1'})
if(!worksheet["!cols"]) {
worksheet["!cols"] = []
}
worksheet["!cols"][0] = {wch: 20}
worksheet["!cols"][1] = {wch: 20}
const workbook = XLSX.utils.book_new();
if(!workbook.Custprops) {
workbook.Custprops = {}
}
let token = `${row_data.supplier_id}ji${row_data.id}fu${row_data.user_id}jf${row_data.order_id}`
workbook.Custprops["type"] = 'simple';
workbook.Custprops["inquery"] = row_data.id;
workbook.Custprops["supplier"] = row_data.supplier_id;
workbook.Custprops["link"] = row_data.link;
workbook.Custprops["order_no"] = row_data.order_no;
workbook.Custprops["token"] = md5(token);
XLSX.utils.book_append_sheet(workbook, worksheet, `${seller_name}询价函`);
var time = new Date();
var ext = `${row_data.supplier_name}-${time.getFullYear()}${time.getMonth() + 1}${time.getDate()}-${Math.round(Math.random() * 10000)}`;
let fn = `${ext}.xlsx`
XLSX.writeFile(workbook,fn,{compression: true});
await $actionLog(props.orderId,user_store.name,'生成并下载简易询价函','询价函id=' + row_data.id)
if(row_data.status != 2) {
await $sendLink(row_data.id)
row_data.status = 1
}
ElMessage({
message: "简易询价函已下载!",
type: 'success',
})
} else {
ElMessage({
message: "没有需要询价的产品!",
type: 'warning',
})
}
}
*/
// const MAP_KEYS = ['','','','','','','','']
// const MAP_NAME = ['code','brand','amount','price','market','new_code','note','nums']
//
// const uploadExcel = async (row_data) => {
// const [hFile] = await window.showOpenFilePicker({
// types: [{
// description: '',
// accept: {
// 'application/vnd.ms-excel': ['.xlsx', '.xls', /*...*/ ]
// }
// }],
// excludeAcceptAllOption: true,
// multiple: false
// });
// const file = await (await hFile.getFile()).arrayBuffer();
// const wb = XLSX.read(file);
// let token = md5(`${row_data.supplier_id}ji${row_data.id}fu${row_data.user_id}jf${row_data.order_id}`)
// if(wb.Custprops["inquery"] == row_data.id && wb.Custprops["supplier"] == row_data.supplier_id && wb.Custprops["link"] == row_data.link && wb.Custprops["order_no"] == row_data.order_no && wb.Custprops["token"] == token) {
// const sheet = wb.SheetNames[0]
// if(wb.Custprops["type"] == 'simple') {
// const price_data = XLSX.utils.sheet_to_json(wb.Sheets[sheet], {header: "A"})
// let product = row_data.products
// price_data.forEach(item => {
// let product_data = product.find(val => val.code == item['A'])
// if(product_data) {
// product_data.price = item['C'] ?? 0.0
// product_data.note = item['D'] ?? ''
// product_data.nums = item['E'] ?? 0
// }
// })
// const {data:res} = await $enquiryReturn(row_data.id,product,token)
// if(res.data.result) {
// row_data.status = 2
// row_data.return_data = product
// ElMessage({
// message: "",
// type: 'success',
// })
// } else {
// ElMessage({
// message: "!",
// type: 'error',
// })
// }
// } else if(wb.Custprops["type"] == 'full') {
// const price_data = XLSX.utils.sheet_to_json(wb.Sheets[sheet], {header: "A"})
// price_data.splice(0,3)
// let product = row_data.products
// price_data.forEach(item => {
// let product_data = product.find(val => val.code == item['B'])
// if(product_data) {
// product_data.new_code = item['C'] ?? ''
// product_data.market = item['F'] ?? 0.0
// product_data.price = item['G'] ?? 0.0
// product_data.price2 = item['H'] ?? 0.0
// product_data.nums = item['I'] ?? 0
// product_data.note = item['J'] ?? ''
// }
// })
// const {data:res} = await $enquiryReturn(row_data.id,product,token)
// if(res.data.result) {
// row_data.status = 2
// row_data.return_data = product
// ElMessage({
// message: "",
// type: 'success',
// })
// } else {
// ElMessage({
// message: "!",
// type: 'error',
// })
// }
// } else {
// ElMessage({
// message: "",
// type: 'warning',
// })
// }
// } else {
// ElMessage({
// message: "",
// type: 'warning',
// })
// }
// }
</script>
<style scoped>
:deep(.no-expand-icon .el-table__expand-icon) {
display: none;
}
</style>

517
src/components/Offer.vue Normal file
View File

@ -0,0 +1,517 @@
<template>
<div v-if="hasEnquery">
<el-row style="margin-top: 20px;">
<el-col :span="16">
<el-text v-if="offer.offer.length" type="primary" tag="b">已完成报价单报价日期{{offer.offer_time}}</el-text>
</el-col>
<el-col :span="8">
<el-button v-if="offer.enquiry_return>0" color="#e85827" @click="initPreset" size="large" :icon="ShoppingCartFull" >批量预报价</el-button>
<el-button v-if="offer.enquiry_return>0" color="#485827" @click="resetPrice" size="large" :icon="Money" >低价预选</el-button>
<el-button :disabled="!hasChange" type="primary" size="large" :icon="Money" @click="createOrderOffer"><span v-if="offer.offer.length">修改</span><span v-else>新建</span>报价单</el-button>
</el-col>
</el-row>
<el-table
ref="orderResultTableRef"
fit
size="small"
class="w-full text-xs mt-2"
empty-text="无供应商报价"
style="border: 1px solid black;"
:header-cell-style="{background: '#e0f2fe',color: 'black',borderColor: 'black'}"
:cell-style="{borderColor: 'black'}"
border
:height="800"
:data="product"
>
<el-table-column fixed label="#" type="index" width="50" align="center" header-align="center"/>
<el-table-column prop="code" label="产品编码" width="120" header-align="center" show-overflow-tooltip />
<el-table-column prop="b_name" label="品牌" width="100" align="center" header-align="center"/>
<el-table-column prop="price" label="面价" header-align="center" align="right" width="100" />
<el-table-column prop="amount" label="购买数" width="100" header-align="center" align="center">
<template #default="{row}">
<el-input-number v-model="row.amount" :min="0" size="small" controls-position="right" style="width:80px;"/>
</template>
</el-table-column>
<el-table-column label="商家备注" header-align="center" width="180" align="center">
<template #default="{row}">
<el-input v-model="row.note2" clearable size="small" type="textarea" autosize></el-input>
</template>
</el-table-column>
<!-- <el-table-column prop="cost_price" label="选中报价(元)" header-align="center" align="right" width="120px">
<template #default="{row}">
<el-text>{{ parseFloat(row.cost_price).toFixed(2) }}</el-text>
</template>
</el-table-column> -->
<el-table-column label="客户报价(元)" header-align="center" align="right" width="140">
<template #default="scope">
<div @click="handlePriceChange(scope.row,scope.$index)" class="cursor-pointer">
<div class="flex-col font-bold">
<div>
<el-text type="primary" tag="b" size="small">{{parseFloat(scope.row.customer_price).toFixed(2)}}</el-text>
</div>
<div class="mt-[0px]">
<el-text class="mt-[5px]" type="danger" tag="b" size="small">{{ parseFloat(scope.row.cost_price).toFixed(2) }}</el-text>
</div>
<div class="mt-[0px]">
<el-text class="mt-[5px]" type="success" tag="b" size="small">加点{{ Math.round(parseFloat(scope.row.customer_price) * 100 / parseFloat(scope.row.cost_price)) - 100 }}</el-text>
</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="紧急报价(元)" header-align="center" align="right" width="120">
<template #default="scope">
<el-button
text
@click="handlePriceChange(scope.row,scope.$index)"
>
<div class="flex-col font-bold" v-if="parseFloat(scope.row.cost_price2) > 0.0">
<div>
<el-text type="primary" tag="b" size="small">{{parseFloat(scope.row.customer_price2).toFixed(2)}}</el-text>
</div>
<div class="mt-[5px]">
<el-text class="mt-[5px]" type="danger" tag="b" size="small">{{ parseFloat(scope.row.cost_price2).toFixed(2) }}</el-text>
</div>
</div>
<!-- <div v-if="parseFloat(scope.row.cost_price2) > 0.0">
{{parseFloat(scope.row.customer_price2).toFixed(2)}}/<el-text type="danger" tag="b">{{ parseFloat(scope.row.cost_price2).toFixed(2) }}</el-text>
</div> -->
</el-button>
</template>
</el-table-column>
<el-table-column label="供应商及商城报价" header-align="center">
<template #header>
<div style="width: 100%;display: inline-grid;align-items: center;grid-template-columns: 50px repeat(8,1fr);">
<div class="text-center">选项</div>
<div class="text-center" style="border-right: 1px solid black;border-left: 1px solid black;">供应商</div>
<div class="text-center" style="border-right: 1px solid black;">面价()</div>
<div class="text-center" style="border-right: 1px solid black;">报价()</div>
<div class="text-center" style="border-right: 1px solid black;">急件价()</div>
<div class="text-center" style="border-right: 1px solid black;">库存</div>
<div class="text-center" style="border-right: 1px solid black;">货源</div>
<div class="text-center" style="border-right: 1px solid black;">货期</div>
<div class="text-center">备注</div>
</div>
</template>
<template #default="scope">
<el-table
:data="scope.row.enquiry2"
:show-header="false"
size="small"
class="w-auto text-xs"
empty-text="无供应商报价"
:cell-style="{borderColor: 'black'}"
border
style="margin:0;padding: 0;"
>
<el-table-column width="50" align="center">
<template #default="{row}">
<el-radio :label="row.id" v-model="scope.row.selected" @change="handleSupplierChange(scope.row,row)" :disabled="parseFloat(row.price)==0"><span></span></el-radio>
</template>
</el-table-column>
<el-table-column prop="id" label="供应商" header-align="center">
<template #default="{row}">
<el-text :tag="row.id == scope.row.selected ? 'b' : 'i'" size="small">{{ supplierName(row.id) }}</el-text>
</template>
</el-table-column>
<el-table-column prop="market" label="面价(元)" header-align="center" align="right">
<template #default="{row}">
<el-text v-if="row.market" :tag="row.id == scope.row.selected ? 'b' : 'i'" size="small">{{ parseFloat(row.market).toFixed(2) }}</el-text>
</template>
</el-table-column>
<el-table-column prop="price" label="报价(元)" header-align="center" align="right">
<template #default="{row}">
<el-text :tag="row.id == scope.row.selected ? 'b' : 'i'" size="small">{{ parseFloat(row.price).toFixed(2) }}</el-text>
</template>
</el-table-column>
<el-table-column prop="price2" label="急件价(元)" header-align="center" align="right">
<template #default="{row}">
<el-text :tag="row.id == scope.row.selected ? 'b' : 'i'" size="small">{{ parseFloat(row.price2).toFixed(2) }}</el-text>
</template>
</el-table-column>
<el-table-column prop="store" label="库存" header-align="center" align="right">
<template #default="{row}">
<el-text :tag="row.id == scope.row.selected ? 'b' : 'i'" size="small" align="right">{{ row.store }}</el-text>
</template>
</el-table-column>
<el-table-column prop="source" label="货源" header-align="center">
<template #default="{row}">
<el-text :tag="row.id == scope.row.selected ? 'b' : 'i'" size="small">{{ row.source }}</el-text>
</template>
</el-table-column>
<el-table-column prop="store" label="货期" header-align="center" align="right">
<template #default="{row}">
<el-text :tag="row.id == scope.row.selected ? 'b' : 'i'" size="small">{{ row.period }}</el-text>
</template>
</el-table-column>
<el-table-column prop="note" label="备注" header-align="center">
<template #default="{row}">
<el-text :tag="row.id == scope.row.selected ? 'b' : 'i'" size="small">{{ row.note }}</el-text>
</template>
</el-table-column>
</el-table>
</template>
</el-table-column>
</el-table>
</div>
<el-dialog
v-model="presetDialogVisible"
title="预报价参数"
width="27%"
destroy-on-close
center
:draggable="true"
>
<el-table :data="points">
<el-table-column label="默认" prop="default" header-align="center" align="center" width="80">
<template #default="{row}">
<el-radio :label="row.id" v-model="selected" size="large"><span></span></el-radio>
</template>
</el-table-column>
<el-table-column label="供应商" prop="id" header-align="center">
<template #default="{row}">
<el-text tag="b">{{ supplierName(row.id) }}</el-text>
</template>
</el-table-column>
<el-table-column label="报价加点" prop="point" header-align="center" width="160">
<template #default="{row}">
<el-input-number
v-model="row.point"
controls-position="right"
size="small"
>
</el-input-number>
</template>
</el-table-column>
</el-table>
<template #footer>
<span class="dialog-footer">
<el-button type="success" @click="presetPoint" :icon="Setting">预设报价参数</el-button>
</span>
</template>
</el-dialog>
<el-dialog
v-model="calcpriceDialogVisible"
title="价格计算器"
width="35%"
destroy-on-close
center
:draggable="true"
>
<el-table :data="[calcPrice]">
<el-table-column label="供应商价" prop="cost" align="center">
<template #default="{row}">
<el-text type="primary">{{parseFloat(row.cost).toFixed(2)}}</el-text>
</template>
</el-table-column>
<el-table-column label="定价策略" prop="type" align="center">
<template #default="{row}">
<el-select v-model="row.type" placeholder="请选择定价策略">
<el-option label="手动价格(=)" value="fixed"></el-option>
<el-option label="阶梯加价(+)" value="add"></el-option>
<el-option label="固定加点(+%)" value="percent"></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="价格参数" prop="point" align="center">
<template #default="{row}">
<el-input v-model="row.point"></el-input>
</template>
</el-table-column>
<el-table-column label="客户报价" prop="customer" align="center">
<template #default="{row}">
<el-text tag="b">{{ price_calculator }}</el-text>
</template>
</el-table-column>
</el-table>
<template #footer>
<span class="dialog-footer">
<el-button type="success" @click="setPrice()" :icon="EditPen">设定客户报价</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { ref,onMounted,computed } from 'vue'
import { $actionLog,$makeOrderOffer,$orderEnqueryResult,shopList,Suppliers } from '@/api'
import { useRouter } from 'vue-router'
import { Money,Plus,Open,TurnOff,ShoppingCartFull,Setting,EditPen } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { md5 } from '@/utils'
import { useUserStore } from '@/store'
const user_store = useUserStore()
const props = defineProps({
'orderId': {
type: Number,
default: 0
},
'orderStatus': {
type: String,
default: 0
}
})
const router = useRouter()
const emit = defineEmits(['next-step','add-goods','make-order'])
const order_id = ref(0)
const orderStatus = ref(parseInt(props.orderStatus))
const offer = ref({})
const product = ref([])
const productHash = ref('')
const hasEnquery = ref(false)
const preset = ref({})
const points = ref([])
onMounted(async () => {
order_id.value = props.orderId
let {data: res} = await $orderEnqueryResult(order_id.value)
if(res.data && res.data.product) {
offer.value = res.data
product.value = res.data.product
points.value = []
offer.value.supplier_list.forEach(item => points.value.push({id: item,point:5,default: false}))
if(points.value.length>0) {
points.value[0].default = true
}
product.value.forEach((item,idx) => {
item.note2 = offer.value.offer[idx] ? offer.value.offer[idx].note : ''
item.enquiry2 = []
let ids = Object.keys(item.enquiry)
let vals = Object.values(item.enquiry)
for(let i=0;i<ids.length;i++) {
item.enquiry2.push({
id: ids[i],
price: parseFloat(vals[i][0]) ? Math.round(parseFloat(vals[i][0]) * 100) / 100 : 0.00,
price2: vals[i].length < 4 ? 0.0 : parseFloat(vals[i][3]) ? Math.round(parseFloat(vals[i][3]) * 100) / 100 : 0.00,
store: vals[i][1],
note: vals[i][2],
source: vals[i].length < 5 ? '' : vals[i][4]
})
}
})
productHash.value = md5(JSON.stringify(product.value))
for(var i=0;i<product.value.length;i++) {
if(Object.keys(product.value[i].enquiry).length > 0) {
hasEnquery.value = true
break
}
}
}
const {data:supplier} = await Suppliers(offer.value.supplier_list)
supplier_list.value = supplier.data.map(item => ({id:item.sid,name:item.sname,mobile:item.mobile}))
const {data:seller} = await shopList(offer.value.shop_list)
seller_list.value = seller.data.map(item => ({id:`G${item.id}`,name:`*商城*${item.seller_name}`,mobile:item.mobile}))
})
const resetPrice = () => {
product.value.forEach(item => {
let price_arr = []
item.enquiry2.forEach(m => {
if(!`${m.id}`.startsWith('G') && m.price > 0) {
price_arr.push(m.price)
}
})
let select_item = item.enquiry2.find((m) => {
if(!`${m.id}`.startsWith('G') && m.price == Math.min(...price_arr)) {
return true
}
})
if(select_item && select_item.id) {
item.selected = select_item.id
item.cost_price = select_item.price
item.cost_price2 = select_item.price2
item.note2 = select_item.note
let pt = points.value.find(p => p.id == item.selected)
if(pt && pt.id) {
item.customer_price = Math.ceil(select_item.price * (100 + pt.point) / 100)
item.customer_price2 = Math.ceil(select_item.price2 * (100 + pt.point) / 100)
} else {
item.customer_price = Math.ceil(select_item.price)
item.customer_price2 = Math.ceil(select_item.price2)
}
}
})
}
const supplier_list = ref([])
const seller_list = ref([])
const supplierName = computed(() => (id) => {
if(`${id}`.startsWith('G')) {
let ids = `${id}`.split('-')
let idx = seller_list.value.findIndex(item => item.id == ids[0])
if(idx != -1) {
return seller_list.value[idx].name
}
} else {
let idx = supplier_list.value.findIndex(item => item.id == id)
if(idx != -1) {
return supplier_list.value[idx].name
}
}
return 'unknown'
})
const enquiryData = computed(() => (obj) => {
let ids = Object.keys(obj)
let newData = []
for(let i=0;i<ids.length;i++) {
newData.push({
id:ids[i],
price:obj[ids[i]][0],
store:obj[ids[i]][1],
note:obj[ids[i]][2],
})
}
return newData
})
const hasChange = computed(() => productHash.value != md5(JSON.stringify(product.value)) )
const handleSupplierChange = (row,select_item) => {
row.cost_price = select_item.price
row.cost_price2 = select_item.price2
row.note2 = select_item.note
let pt = points.value.find(p => p.id == row.selected)
if(pt && pt.id) {
row.customer_price = Math.ceil(select_item.price * (100 + pt.point) / 100)
row.customer_price2 = Math.ceil(select_item.price2 * (100 + pt.point) / 100)
} else {
row.customer_price = Math.ceil(select_item.price)
row.customer_price2 = Math.ceil(select_item.price2)
}
}
const calcpriceDialogVisible = ref(false)
const handlePriceChange = (row,index) => {
if(!row.selected || row.selected == 0) {
row.cost_price = 0.00
row.cost_price2 = 0.00
row.customer_price = 0.00
row.customer_price2 = 0.00
} else {
calcpriceDialogVisible.value = true
row.cost_price = row.enquiry[row.selected][0]
row.cost_price2 = row.enquiry[row.selected].length < 4 ? 0.0 : row.enquiry[row.selected][3]
calcPrice.value.cost = row.cost_price
calcPrice.value.cost2 = row.cost_price2
calcPrice.value.index = index
}
}
const calcPrice = ref({
cost: 0.0,
cost2: 0.0,
type: 'percent',
point: 0.0,
customer: 0.0,
customer2: 0.0,
index: 0
})
const price_calculator = computed(() => {
calcPrice.value.customer = 0.0
calcPrice.value.customer2 = 0.0
if(calcPrice.value.type == 'fixed') {
calcPrice.value.customer = Math.ceil(parseFloat(calcPrice.value.point))
calcPrice.value.customer2 = Math.ceil(parseFloat(calcPrice.value.point))
} else if(calcPrice.value.type == 'add') {
calcPrice.value.customer = Math.ceil(parseFloat(calcPrice.value.point) + parseFloat(calcPrice.value.cost))
calcPrice.value.customer2 = Math.ceil(parseFloat(calcPrice.value.point) + parseFloat(calcPrice.value.cost2))
} else if(calcPrice.value.type == 'percent') {
calcPrice.value.customer = Math.ceil((1.0 + parseFloat(calcPrice.value.point) / 100.00) * parseFloat(calcPrice.value.cost))
calcPrice.value.customer2 = Math.ceil((1.0 + parseFloat(calcPrice.value.point) / 100.00) * parseFloat(calcPrice.value.cost2))
}
return calcPrice.value.customer
})
const setPrice = () => {
product.value[calcPrice.value.index].customer_price = calcPrice.value.customer
product.value[calcPrice.value.index].customer_price2 = calcPrice.value.customer2
calcpriceDialogVisible.value = false
}
const presetDialogVisible = ref(false)
const selected = ref(0)
const initPreset = () => {
selected.value = points.value[0].id
presetDialogVisible.value = true
}
const presetPoint = () => {
preset.value = {}
points.value.forEach(item => {
preset.value[`${item.id}`] = parseInt(item.point)
if(item.default) {
preset.value.default = item.id
}
})
preset.value.default = selected.value
product.value.forEach(item => {
if(item.enquiry[preset.value.default]) {
item.selected = preset.value.default
item.note2 = item.enquiry[item.selected][2]
let cost = item.enquiry[item.selected][0]
let cost2 = item.enquiry[item.selected].length < 4 ? 0.0 : item.enquiry[item.selected][3]
item.cost_price = cost
item.cost_price2 = cost2
item.customer_price = Math.ceil(cost * (100 + preset.value[`${item.selected}`]) / 100.00)
item.customer_price2 = Math.ceil(cost2 * (100 + preset.value[`${item.selected}`]) / 100.00)
} else {
item.selected = 0
item.note2 = ''
item.cost_price = 0.00
item.cost_price2 = 0.00
item.customer_price = 0.00
item.customer_price2 = 0.00
}
})
presetDialogVisible.value = false
}
const createOrderOffer = async () => {
let data = product.value.map(
(item,idx) => (
{
code:item.code,
name:item.name,
brand:item.brand,
brand_name: item.b_name,
market_price:item.price,
sell_price: item.customer_price,
sell_price2: item.customer_price2,
amount: item.amount,
selected: item.selected,
from: item.selected != 0 ? item.enquiry[item.selected] : [0,0,'',0],
store: Object.values(item.enquiry).reduce((prev,cur) => prev + parseInt(cur[1]),0),
note: item.note2 ?? ''
}
)
)
const {data:res} = await $makeOrderOffer(order_id.value,data)
if(res.data) {
let action = '创建用户报价单'
if(offer.value.offer) {
action = '修改用户报价单'
}
await $actionLog(order_id.value,user_store.name,action,JSON.stringify(data))
ElMessage({
message: `成功${action}!`,
type: 'success',
})
reload()
}
productHash.value = md5(JSON.stringify(product.value))
emit('next-step',order_id.value)
}
const reload = async () => {
let {data: res} = await $orderEnqueryResult(order_id.value)
if(res.data) {
offer.value = res.data
}
}
</script>

View File

@ -0,0 +1,956 @@
<template>
<el-row v-if="order_id">
<el-col :span="18" style="text-align: left;">
<el-form inline>
<el-form-item label="订单号">
<el-text type="primary" tag="b">{{ order_no }}</el-text>
</el-form-item>
<el-form-item label="客户">
<span v-if="enableCustomerEdit">
<el-autocomplete
v-model="customer_search"
:fetch-suggestions="customerSearch"
@select="updateCustomer"
clearable
class="inline-input w-50"
placeholder="名称关键字或手机号"
/>
<span class="ml-8px">
<el-button size="small" type="danger" circle @click="() => enableCustomerEdit = false"><i class="fa fa-close"></i></el-button>
</span>
</span>
<span v-else>
<el-button text type="primary" @click="handleCustomerEdit">
{{ userInfo.username }}
<el-icon class="el-icon--right"><EditPen /></el-icon>
</el-button>
</span>
</el-form-item>
<el-form-item label="订单备注">
<el-input v-model="order_note" @change="handleNoteChange" />
</el-form-item>
<el-form-item label="订单主题">
<el-input v-model="order_subject" @change="handleSubjectChange" />
</el-form-item>
</el-form>
</el-col>
<el-col :span="6">
<el-button color="#0f6b9c" v-if="order_status==0" :disabled="goodsList.length==0 || !goods_change" @click="saveOrderGoods" :icon="ShoppingCartFull" size="large">
保存商品
</el-button>
<slot name="home-button"></slot>
</el-col>
</el-row>
<el-row v-else>
<el-col :span="8" :offset="8">
<el-form>
<el-form-item label="客户选择">
<el-autocomplete
v-model="customer_search"
:fetch-suggestions="customerSearch"
@select="handleCustomerSelect"
clearable
class="inline-input w-50"
placeholder="名称关键字或手机号"
/>
<el-text style="font-weight: 600;margin-left: 20px;" type="primary">{{ userInfo.username }}</el-text>
</el-form-item>
<el-form-item label="订单备注">
<el-input v-model="order_note" />
</el-form-item>
<el-form-item label="订单主题">
<el-input v-model="order_subject"/>
</el-form-item>
<el-form-item>
<div class="flex justify-center w-full">
<el-button color="#b23b2f" @click="createOrder" :icon="EditPen" size="large" :disabled="!userInfo.id">创建订单</el-button>
</div>
</el-form-item>
</el-form>
</el-col>
<el-col :span="4">
</el-col>
<el-col :span="4">
<slot name="home-button"></slot>
</el-col>
</el-row>
<hr style="margin: 20px 0;" v-if="order_id">
<div v-if="order_id">
<div v-if="order_status==0">
<el-button type="primary" :icon="DocumentCopy" @click="fileInput">批量导入产品</el-button>
</div>
<div class="flex w-full">
<el-table
ref="goodsTableRef"
fit
:data="goodsList"
class="mt-4 text-xs w-auto"
:header-cell-style="{background: '#e0f2fe', color: 'black',borderColor: 'black'}"
:cell-style="{borderColor: 'black'}"
style="border: 1px solid black;"
size="small"
@row-click="editRow"
border
>
<el-table-column fixed label="#" type="index" width="50" align="center" header-align="center" />
<el-table-column prop="b_name" label="品牌" header-align="center" width="80" />
<el-table-column prop="code" label="件号" width="120" show-overflow-tooltip header-align="center">
<template #default="scope">
<el-popover effect="light" trigger="hover" placement="left-start" width="auto">
<template #default>
<div>代码: {{ scope.row.code }}</div>
<div>品名: {{ scope.row.name }}</div>
<div>品牌: {{ scope.row.b_name }}</div>
<div>数量: {{ scope.row.amount }}</div>
<div>备注: {{ scope.row.note }}</div>
</template>
<template #reference>
<el-text size="small" tag="b">{{ scope.row.code }}</el-text>
</template>
</el-popover>
</template>
</el-table-column>
<el-table-column prop="new_code" label="更新件号" header-align="center" width="110" show-overflow-tooltip>
<template #default="{row}">
<el-text size="small">{{ row.new_code }}</el-text>
</template>
</el-table-column>
<el-table-column label="名称" width="140" show-overflow-tooltip header-align="center">
<template #default="scope">
<el-text size="small">{{ scope.row.name }}</el-text>
</template>
</el-table-column>
<el-table-column prop="price" label="面价" header-align="center" align="right" width="110">
<template #default="scope">
<el-text size="small" tag="b" type="primary">{{ scope.row.price }}</el-text>
</template>
</el-table-column>
<el-table-column prop="add_time" label="价格版本" header-align="center" align="center" width="110" show-overflow-tooltip>
<template #default="{row}">
<el-text size="small" tag="b" type="danger">{{ row.add_time }}</el-text>
</template>
</el-table-column>
<el-table-column prop="sell_price" label="预估价" header-align="center" align="right" width="110">
<template #default="scope">
<el-text size="small" tag="b" type="success">{{ scope.row.sell_price }}</el-text>
</template>
</el-table-column>
<el-table-column prop="weight" label="重量" header-align="center" align="right" width="80">
<template #default="scope">
<el-text size="small">{{ scope.row.weight }}</el-text>
</template>
</el-table-column>
<el-table-column prop="volume" label="体积" header-align="center" align="right" width="80">
<template #default="scope">
<el-text size="small">{{ scope.row.volumn }}</el-text>
</template>
</el-table-column>
<el-table-column prop="amount" label="数量" width="60" header-align="center" align="center"/>
<el-table-column prop="note" label="备注" header-align="center" width="140" show-overflow-tooltip/>
<el-table-column prop="seller" label="商品来源" width="100" show-overflow-tooltip header-align="center" align="center">
<template #default="{row}">
<el-popover v-if="row.goods && row.goods.length" effect="light" trigger="hover" placement="left-start" width="auto">
<template #default>
<el-table
fit
:data="row.goods"
:height="240"
style="width: 400px;"
border
>
<el-table-column prop="seller_name" label="商户" show-overflow-tooltip />
<el-table-column prop="up_time" label="上架时间" show-overflow-tooltip align="center">
<template #default="{row}">
{{ dayjs(row.up_time).format('YY-MM-DD') }}
</template>
</el-table-column>
<el-table-column prop="store_nums" label="库存" show-overflow-tooltip width="80px" align="right" />
<el-table-column prop="sell_price" label="售价" width="100px" align="right">
<template #default="{row}">
<el-text :type="row.no_tax == '0' ? 'info' : 'danger'" tag="b">{{ row.sell_price }}</el-text>
</template>
</el-table-column>
</el-table>
</template>
<template #reference>
<el-text type="danger" tag="b" size="small">商城有货</el-text>
</template>
</el-popover>
<span v-else>
<el-text v-if="row.seller_id>0" tag="b">{{ row.seller }}</el-text>
<el-text v-else-if="row.type=='product'" type="success" tag="b" size="small">{{ row.seller }}</el-text>
<el-text v-else-if="row.type=='goods'" type="primary" tag="b" size="small">{{ row.seller }}</el-text>
<el-text v-else-if="row.type=='other'" type="warning" tag="b" size="small">{{ row.seller }}</el-text>
</span>
</template>
</el-table-column>
<el-table-column label="" header-align="center" align="center" v-if="order_status==0" width="280">
<template #default="scope">
<el-button
size="small"
type="primary"
@click.stop="editRow(scope.row)"
>
<el-icon color="#FFF">
<EditPen />
</el-icon>
</el-button>
<el-button
size="small"
:type="scope.row.focus ? 'danger' : 'success'"
:disabled="scope.row.focus"
@click.stop="setFocusItem(scope.row,scope.$index)"
>
<el-icon color="#FFF">
<FocusIn />
</el-icon>
</el-button>
<el-button
size="small"
type="primary"
:disabled="scope.$index==0"
:icon="ArrowUpBold"
@click.stop="moveUp(scope.$index,scope.row)"
/>
<el-button
size="small"
type="primary"
:disabled="scope.$index==(goodsList.length-1)"
:icon="ArrowDownBold"
@click.stop="moveDown(scope.$index,scope.row)"
/>
<el-button
size="small"
type="danger"
:icon="Delete"
@click.stop="()=>goodsList.splice(scope.$index,1)"
/>
</template>
</el-table-column>
</el-table>
</div>
<div class="w-full flex gap-2 items-center mt-4 justify-around bg-sky-50 p-2" v-if="order_status==0">
<div class="flex items-center">
<div class="font-bold">品牌</div>
<BrandPicker v-model="brandInfo" class="inline-input w-40 ml-2" :width="120" :font="12" />
</div>
<div class="flex items-center">
<div class="font-bold">件号</div>
<ProductPicker v-model.trim="product_code" class="inline-input w-40 ml-2" @selected="handleProductSelect" :width="120" :font="12" />
</div>
<div class="flex items-center">
<div class="font-bold">更新件号</div>
<div class="ml-2">
<el-input v-model="edit_data.new_code" clearable class="inline-input" style="width: 140px;font-size: 12px;"></el-input>
</div>
</div>
<div class="flex items-center">
<div class="font-bold">名称</div>
<div class="ml-2">
<el-input v-model="edit_data.name" clearable class="inline-input" style="width: 140px;font-size: 12px;"></el-input>
</div>
</div>
<div class="flex items-center">
<div class="font-bold">面价</div>
<div class="ml-2">
<el-input v-model="edit_data.price" :input-style="{'text-align': 'right'}" style="width: 90px;font-size: 12px;"></el-input>
</div>
</div>
<div class="flex items-center">
<div class="font-bold">预估价</div>
<div class="ml-2">
<el-input v-model="edit_data.sell_price" :input-style="{'text-align': 'right'}" style="width: 90px;font-size: 12px;"></el-input>
</div>
</div>
<div class="flex items-center">
<div class="font-bold">重量</div>
<div class="ml-2">
<el-input v-model="edit_data.weight" :input-style="{'text-align': 'right'}" style="width: 80px;font-size: 12px;"></el-input>
</div>
</div>
<div class="flex items-center">
<div class="font-bold">体积</div>
<div class="ml-2">
<el-input v-model="edit_data.volumn" :input-style="{'text-align': 'right'}" style="width: 80px;font-size: 12px;"></el-input>
</div>
</div>
<div class="flex items-center">
<div class="font-bold">数量</div>
<div class="ml-2">
<el-input v-model.number="edit_data.amount" :input-style="{'text-align': 'right'}" style="width: 80px;font-size: 12px;"></el-input>
</div>
</div>
<div class="flex items-center">
<div class="font-bold">备注</div>
<div class="ml-2">
<el-input v-model="edit_data.note" clearable class="inline-input" style="width: 160px;font-size: 12px;"></el-input>
</div>
</div>
<div class="flex items-center">
<el-button
size="default"
type="primary"
color="#0f6b9c"
:icon="Plus"
:disabled="!product_code || !brandInfo.id"
@click="submit_product"
>添加
</el-button>
<el-button
size="default"
type="danger"
:icon="Delete"
@click="clearInput"
class="ml-4"
>擦除
</el-button>
</div>
</div>
</div>
<el-dialog
v-model="centerDialogVisible"
title="更新预订商品信息"
width="30%"
destroy-on-close
center
>
<el-card class="box-card-edit">
<el-form :inline="true">
<el-form-item label="产品编码">
<el-input v-model="new_product.code" placeholder="产品编码" />
</el-form-item>
<el-form-item label="更新件号">
<el-input v-model="new_product.new_code" placeholder="更新件号" />
</el-form-item>
<el-form-item label="商品名称">
<el-input v-model="new_product.name" placeholder="商品名称" />
</el-form-item>
<el-form-item label="商品品牌">
<BrandPicker v-model="new_product.brand_info" class="inline-input w-40" />
</el-form-item>
<el-form-item label="商品面价">
<el-input v-model="new_product.price" placeholder="商品面价" />
</el-form-item>
<el-form-item label="商品售价">
<el-input v-model="new_product.sell_price" placeholder="商品售价" />
</el-form-item>
<el-form-item label="购买数量">
<el-input-number v-model="new_product.amount" :min="1" />
</el-form-item>
<el-form-item label="商品备注">
<el-input v-model="new_product.note" placeholder="商品备注" />
</el-form-item>
</el-form>
</el-card>
<template #footer>
<span class="dialog-footer">
<el-button type="success" @click="editProduct" :icon="UploadFilled">确认更新</el-button>
</span>
</template>
</el-dialog>
<el-dialog
v-model="focusDialogVisible"
title="大件跟踪"
width="30%"
destroy-on-close
center
>
<el-card class="box-card-edit">
<template #header>
<div class="card-header">
<span>跟踪{{`${focusInfo.b_name} ${focusInfo.code}`}}</span>
</div>
</template>
<el-form :inline="true">
<el-form-item label="更新件号">
<el-input v-model="focusInfo.new_code" placeholder="更新件号" />
</el-form-item>
<el-form-item label="产品面价">
<el-input v-model="focusInfo.price" />
</el-form-item>
</el-form>
<el-form style="margin-top: 20px;">
<el-form-item label="跟踪备注">
<el-input v-model="focusInfo.note" placeholder="跟踪备注" />
</el-form-item>
</el-form>
</el-card>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="handleFocusItem" :icon="EditPen">
备注完成
</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { ref,reactive,computed,nextTick,onMounted,onUnmounted } from 'vue'
import { EditPen,MessageBox,Plus,Delete,ArrowUpBold,ArrowDownBold,DocumentCopy,UploadFilled,ShoppingCartFull } from '@element-plus/icons-vue'
import FocusIn from '@/components/icons/FocusIn.vue'
import dayjs from 'dayjs'
import {md5} from '@/utils'
import * as XLSX from 'xlsx'
import { memberInfo,searchMember } from '@/api'
import { onlyGoodsByCode,partsGoodsById,productInfo,hasProduct,hasFocus,hasFocusBatch,updateFocus,hasBrandProduct } from '@/api/product'
import { createInquiry,inquiryInfo,inquiryGoodsList,update } from '@/api/inquiry'
import { addLog } from '@/api/user'
import { ElMessage } from 'element-plus'
import BrandPicker from '@/components/BrandPicker.vue'
import ProductPicker from '@/components/ProductPicker.vue'
import { useUserStore } from '@/store'
const user_store = useUserStore()
const order_id = ref(0)
const order_status = ref(0)
const order_no = ref('')
const order_note = ref('')
const order_subject = ref('')
const userInfo = ref({
id: 0,
username: '',
})
const goodsList = ref([])
const goodsHash = ref('')
const emit = defineEmits(['status-change','add-goods','next-step'])
const props = defineProps({
'orderId': {
type:Number,
default: 0
},
'orderStatus': {
type:Number,
default: 0
},
})
onMounted(async() => {
order_id.value = +props.orderId
order_status.value = +props.orderStatus
if(order_id.value != 0) {
const { data:order } = await inquiryInfo(order_id.value)
if(order.data.id && parseInt(order.data.id) == order_id.value) {
order_status.value = +order.data.status
order_no.value = order.data.order_no
order_note.value = order.data.note
order_subject.value = order.data.subject
const { user_id } = order.data
const { data:member } = await memberInfo(user_id)
if(member.data.id) {
userInfo.value = member.data
}
const { data:res } = await inquiryGoodsList(order_id.value)
let data_arr = res.data.map(item => ({brand: item.brand,code: item.code}))
const { data:r } = await hasFocusBatch(data_arr)
console.log(r.data)
// for(let item of res.data) {
// let { data:r } = await hasFocus(item.brand,item.code)
// item.focus = r.data
// }
goodsHash.value = md5(JSON.stringify(res.data))
goodsList.value.push(...res.data)
}
}
})
const goods_change = computed(() => {
return md5(JSON.stringify(goodsList.value)) !== goodsHash.value
})
const orderDialogVisible = ref(false)
const order_info = ref({
order_id: 0,
userInfo: {
id: 0,
username: '',
head_ico: '',
},
sellerInfo: {
seller_name: '',
seller_id: 0,
},
note: '',
subject: ''
})
const customer_search = ref('')
const enableCustomerEdit = ref(false)
const handleCustomerEdit = () => {
enableCustomerEdit.value = true
customer_search.value = ''
}
const customerSearch = async (qs, cb) => {
if(qs && qs.length >= 2) {
try {
const {data:clist} = await searchMember(qs)
var list = clist.data
if (list.length == 0) {
list.push({
id: '-1',
value: '无匹配结果'
})
cb(list);
} else {
list = list.map(item => {
return {
value:`${item.username} ${item.true_name ?? ''} ${item.mobile ?? ''}`,
id: `${item.id}`
}
})
cb(list);
}
} catch (error) {
console.log(error)
}
}
}
const updateCustomer = async (item) => {
if(item.id != -1 && item.id != userInfo.value.id) {
const {data:user} = await memberInfo(item.id)
if(user.data) {
userInfo.value = {...user.data}
}
customer_search.value = ''
await handleCustomerChange()
}
enableCustomerEdit.value = false
}
const handleCustomerSelect = async (item) => {
if(item.id != -1) {
const {data:user} = await memberInfo(item.id)
if(user.data) {
userInfo.value = {...user.data}
}
customer_search.value = ''
}
}
const createOrder = async () => {
if(!userInfo.value.id) {
ElMessage({
message: '创建订单必须先选择客户',
type: 'warning',
})
return
}
let action = '创建用户订单'
const { data:res } = await createInquiry(userInfo.value.id,order_note.value,order_subject.value)
if(res.data.order_id) {
order_id.value = res.data.order_id
order_no.value = res.data.order_no
await addLog(order_id.value,`${user_store.userName}(${user_store.userId})`,action,`客户:${userInfo.value.username}(${userInfo.value.id})`)
ElMessage({
message: '订单创建成功!',
type: 'success',
})
const {data: member} = await memberInfo(userInfo.value.id)
if(member.data) {
userInfo.value = member.data
}
emit('status-change',order_id.value)
} else {
ElMessage({
message: '订单创建失败!',
type: 'warning',
})
}
}
const handleCustomerChange = async () => {
if(order_id.value) {
const {data: res} = await update(order_id.value,{user_id: userInfo.value.id})
await addLog(order_id.value,`${user_store.userName}(${user_store.userId})`,'修改订单客户信息',`${userInfo.value.username}(${userInfo.value.id})`)
ElMessage({
message: '成功修改订单客户!',
type: 'success',
})
emit('status-change',order_id.value)
}
}
const handleNoteChange = async () => {
if(order_id.value) {
await update(order_id.value,{note:order_note.value})
await addLog(order_id.value,`${user_store.userName}(${user_store.userId})`,'修改订单备注',order_note.value)
ElMessage({
message: '成功修改订单备注!',
type: 'success',
})
emit('status-change',order_id.value)
}
}
const handleSubjectChange = async () => {
if(order_id.value) {
await update(order_id.value,{subject:order_subject.value})
await addLog(order_id.value,`${user_store.userName}(${user_store.userId})`,'修改订单主题',order_subject.value)
ElMessage({
message: '成功修改订单主题!',
type: 'success',
})
emit('status-change',order_id.value)
}
}
const baseItem = {
code: '',
new_code: '',
name: '',
b_name: '',
brand: 0,
price: 0,
sell_price: 0,
weight: 0.000,
volumn: 0.000,
amount: 1,
type: 'other',
seller: '待定',
seller_id: 0,
note: ''
}
const brandInfo = ref({})
const product_code = ref('')
const edit_data = ref(baseItem)
let new_product = reactive({
code: '',
new_code: '',
name: '',
brand_info: {id: 0,name: ''},
brand_name: '',
brand: 0,
price: 0.00,
sell_price: 0.00,
amount: 1,
note: '',
add_time: '',
})
const edit_row = ref(0)
const centerDialogVisible = ref(false)
const editRow = (row,column,evt) => {
if(order_status.value > 0) {
return
}
let idx = goodsList.value.findIndex((item) => item.id == row.id && item.code == row.code)
if(idx <= -1) {
return
}
edit_row.value = idx
new_product = reactive({
code: row.code,
new_code: row.new_code,
name: row.name,
brand_info: {id: row.brand,name: row.b_name},
brand_name: row.b_name,
brand: row.brand,
price: row.price,
sell_price: row.sell_price,
amount: row.amount,
note: row.note,
add_time: row.add_time,
})
centerDialogVisible.value = true
}
const editProduct = async () => {
if(!new_product.code || !new_product.brand_info.id) {
return
}
const {data:product} = await hasProduct(new_product.brand_info.id,new_product.code)
if(product.data.bid) {
const {bid:id,code,short_code,cn_name,en_name,brand,b_name,price,dealer_price,order_price: sell_price,weight,volume,add_time} = product.data
let product_data = {
id,
name: cn_name ? cn_name : en_name,
code,
short_code,
new_code: new_product.new_code,
b_name,
brand,
price,
sell_price,
weight,
volume,
type: 'product',
amount: parseInt(new_product.amount) ? parseInt(new_product.amount) : 1,
note: new_product.note,
seller: '预订产品',
seller_id: 0,
add_time,
}
if(new_product.price > 0 && new_product.price != product_data.price) {
product_data.price = new_product.price
}
if(new_product.sell_price > 0 && new_product.sell_price != product_data.sell_price) {
product_data.sell_price = new_product.sell_price
}
Object.assign(goodsList.value[edit_row.value],product_data)
} else {
const {code,name,brand_info,price,sell_price,amount,note} = new_product
let add_data = {
id: 0,
name,
code,
short_code: code.trim().replace('-','').replace(' ','0'),
new_code: new_product.new_code,
brand: brand_info.id,
b_name: brand_info.name,
price,
sell_price,
weight: 0.000,
volume: 0.000,
type:'other',
amount: parseInt(amount) ? parseInt(amount) : 1,
note,
seller: '待定',
seller_id: 0
}
Object.assign(goodsList.value[edit_row.value],add_data)
}
centerDialogVisible.value = false
}
const submit_product = async () => {
if(brandInfo.value.id && edit_data.value.brand != brandInfo.value.id) {
edit_data.value.brand = brandInfo.value.id
edit_data.value.b_name = brandInfo.value.name
}
edit_data.value.goods = []
if(!edit_data.value.id) {
edit_data.value.type = 'other'
edit_data.value.seller = '待定'
edit_data.value.seller_id = 0
edit_data.value.code = product_code.value
edit_data.value.short_code = product_code.value.trim().replace('-','').replace(' ','0')
const {data:goods} = await onlyGoodsByCode(edit_data.value.code,edit_data.value.brand)
if(goods.data && goods.data.length) {
edit_data.value.goods = goods.data
}
} else {
const {data:goods} = await partsGoodsById(edit_data.value.id)
if(goods.data && goods.data.length) {
edit_data.value.goods = goods.data
}
}
pushRow(edit_data.value)
clearInput()
}
const handleProductSelect = async (item) => {
if(item.id > 0) {
const {data:data} = await productInfo(item.id)
if(data.data && data.data.bid) {
const {bid:id,code,short_code,cn_name,en_name,brand,b_name,price,dealer_price,order_price: sell_price,weight,volume,add_time} = data.data
let product_data = {
id,
name: cn_name ? cn_name : en_name,
code,
short_code,
b_name,
brand,
price,
sell_price,
weight,
volume,
type: 'product',
amount: 1,
note: '',
seller: '预订产品',
seller_id: 0,
add_time
}
edit_data.value = product_data
product_code.value = code
brandInfo.value = {id: brand,name: b_name}
}
}
}
const pushRow = (row) => {
if(row.type != 'other') {
let index = goodsList.value.findIndex(item => item.id == row.id && item.type == row.type)
if(index != -1) {
goodsList.value[index]['amount'] += row['amount']
} else {
goodsList.value.push(row)
}
} else {
let index = goodsList.value.findIndex(item => item.code == row.code && item.brand == row.brand && item.type == 'other')
if(index != -1) {
goodsList.value[index]['amount'] += row['amount']
} else {
goodsList.value.push(row)
}
}
}
const clearInput = () => {
nextTick(() => {
edit_data.value = Object.assign({},baseItem)
brandInfo.value = {}
product_code.value = ''
})
}
const moveUp = (index,row) => {
goodsList.value.splice(index-1,0,row)
goodsList.value.splice(index+1,1)
}
const moveDown = (index,row) => {
goodsList.value.splice(index,1)
goodsList.value.splice(index+1,0,row)
}
const saveOrderGoods = async () => {
// await $addOrderGoods(order_id.value,goodsList.value)
// await $actionLog(props.orderId,user_store.name,'',JSON.stringify(goodsList.value))
// goodsHash.value = md5(JSON.stringify(goodsList.value))
ElMessage({
message: "成功修改订单商品!",
type: 'success',
})
emit('status-change',order_id.value)
}
const fileInput = async () => {
const [hFile] = await window.showOpenFilePicker({
types: [{
description: '数据表',
accept: {
'application/vnd.ms-excel': ['.xlsx', '.xls', /*...*/ ]
}
}],
excludeAcceptAllOption: true,
multiple: false
});
const ab = await (await hFile.getFile()).arrayBuffer();
const wb = XLSX.read(ab);
const sheet = wb.SheetNames[0]
let input_data = XLSX.utils.sheet_to_json(wb.Sheets[sheet], {header: "A",raw: false})
input_data = input_data.filter(item => item.A && item.A.trim())
for(let i in input_data) {
let item = input_data[i]
if(item.A) {
let amount = item.C ? isNaN(parseInt(item.C)) ? 1 : parseInt(item.C) : 1
let note = item.D ? item.D : ''
let product_data = {
id: 0,
name: '',
code: item.A,
short_code: item.A.trim().replace('-','').replace(' ','0'),
b_name: '',
brand: 0,
price: 0.00,
sell_price: 0.00,
weight: 0.000,
volume: 0.000,
type: 'other',
amount,
note,
seller: '待定',
seller_id: 0,
}
const {data:data} = await hasBrandProduct(item.B,item.A)
if(data.data && data.data.bid) {
const {bid:id,code,short_code,cn_name,en_name,brand,b_name,price,dealer_price,order_price: sell_price,weight,volume,add_time} = data.data
product_data = {
id,
name: cn_name ? cn_name : en_name,
code: code ? code : item.A,
short_code,
b_name,
brand,
price,
sell_price,
weight,
volume,
type: 'product',
amount,
note,
seller: '预订产品',
seller_id: 0,
add_time,
}
}
product_data.goods = []
if(product_data.id) {
let { data:goods } = await partsGoodsById(product_data.id)
product_data.goods = goods.data
} else {
let { data:goods } = await onlyGoodsByCode(product_data.code,product_data.brand)
product_data.goods = goods.data
}
pushRow(product_data)
}
}
}
const focusDialogVisible = ref(false)
const focusInfo = ref({})
const handleFocusItem = async () => {
let { code,new_code,brand,price,note,index,name } = focusInfo.value
let focus_data = {
code,
brand,
price,
new_code,
name
}
let { data:res } = await updateFocus(focus_data)
if(res.data && res.data.id) {
await addFocusLog(res.data.id,user_store.name,`从订单(${order_id.value})新增,${note}`)
goodsList.value[index].focus = true
}
focusDialogVisible.value = false
}
const setFocusItem = async (row,index) => {
focusInfo.value.index = index
focusInfo.value.code = row.code
focusInfo.value.name = row.name
focusInfo.value.price = row.price
focusInfo.value.brand = row.brand
focusInfo.value.b_name = row.b_name
focusInfo.value.note = ''
focusInfo.value.new_code = null
focusDialogVisible.value = true
}
</script>
<style lang='scss' scoped>
.info-item {
padding-left: 20px;
font-size: 14px;
.title {
font-weight: bold;
}
.info {
color: #d80010;
}
}
.box-card-edit {
max-height: 35vh;
overflow-y: auto;
.focus-line {
margin-top: 10px;
display: grid;
grid-template-columns: 1fr 3fr;
gap: 30px;
.focus-title {
font-weight: bold;
text-align: right;
}
}
.focus-line:last-child {
padding-bottom: 20px;
}
}
</style>

View File

@ -0,0 +1,390 @@
<template>
<el-tabs type="border-card">
<el-tab-pane>
<template #label>
<span style="font-weight: 600;">客户订单</span>
</template>
<div style="margin-top: 20px;text-align: left;" v-if="order_id || (order_offer.goods_array && order_offer.goods_array.length)">
<p>客户名{{order_offer.username}}</p>
<p>订单号{{order_offer.order_no}}</p>
<p>创建日期{{order_offer.create_time}}</p>
<p>客户回复{{order_offer.finish_time}}</p>
<p>收货地址{{order_offer.addr_id != 0 ? `${order_offer.area_name} ${order_offer.address} ${order_offer.accept_name }(${order_offer.mobile})` : '无'}}</p>
<hr>
<div style="margin-top: 20px;text-align: right;width:100%;">
<el-button type="primary" :icon="Document" @click="downloadOfferFile()">下载客户订单</el-button>
</div>
<el-table
:data="order_offer.goods_array"
fit
style="margin-top: 20px;"
:summary-method="getSummaries"
show-summary
>
<el-table-column fixed label="#" width="50px" align="center" halign="center">
<template #default="scope">
{{ scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="code" label="产品编码"/>
<el-table-column prop="brand_name" label="品牌" />
<el-table-column prop="name" label="名称" width="160px" :show-overflow-tooltip="true"/>
<el-table-column prop="market_price" label="面价单价(元)" align="right">
<template #default="{row}">
{{ parseFloat(row.market_price).toFixed(2) }}
</template>
</el-table-column>
<el-table-column prop="sell_price" label="销售单价(元)" align="right">
<template #default="{row}">
{{ parseFloat(row.sell_price).toFixed(2) }}
<el-tag effect="dark" type="danger" v-if="row.urgency"></el-tag>
</template>
</el-table-column>
<el-table-column prop="cost_price" label="成本单价(元)" align="right">
<template #default="{row}">
<el-text type="warning">{{ parseFloat(row.cost_price).toFixed(2) }}</el-text>
</template>
</el-table-column>
<el-table-column prop="amount" label="数量(件/套)" align="right"/>
<el-table-column label="销售总价(元)" align="right">
<template #default="{row}">
{{ (parseFloat(row.sell_price)*parseInt(row.amount)).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="备注">
<template #default="{row}">
{{ row.note ?? '' }}
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
<el-tab-pane>
<template #label>
<span style="font-weight: 600;">采购订单</span>
</template>
<div style="margin-top: 20px;text-align: right;width:100%;">
<el-button type="success" :icon="Document" @click="downloadOrderFile()">下载采购订单</el-button>
</div>
<div style="margin-top: 20px;" v-for="(item,i) in buy_list" :key="i">
<h3>{{ supplierName(i) }}采购清单</h3>
<el-table
:data="item"
fit
:summary-method="orderSummaries"
show-summary
>
<el-table-column fixed label="#" width="50px" align="center" halign="center">
<template #default="scope">
{{ scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="code" label="产品编码"/>
<el-table-column prop="brand_name" label="商品品牌"/>
<el-table-column prop="name" label="商品名称" width="160px" :show-overflow-tooltip="true" />
<el-table-column prop="price" label="采购单价" align="right">
<template #default="{row}">
{{ parseFloat(row.price).toFixed(2) }}
<el-tag effect="dark" type="danger" v-if="row.urgency"></el-tag>
</template>
</el-table-column>
<el-table-column prop="amount" label="采购数量" align="right"/>
<el-table-column label="单项总价" align="right">
<template #default="{row}">
{{ (parseFloat(row.price)*parseInt(row.amount)).toFixed(2) }}
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
</el-tabs>
</template>
<script setup>
import { ref,computed,onMounted } from 'vue'
import { useRoute,useRouter } from 'vue-router'
import { getUserOrder,$actionLog,Suppliers,shopList } from '@/api'
import { Document,Link } from '@element-plus/icons-vue'
import XLSX from 'xlsx-js-style'
import { useUserStore } from '@/store'
import { ElMessage } from 'element-plus'
const emit = defineEmits(['next-step','add-goods','make-order'])
const user_store = useUserStore()
const route = useRoute()
const router = useRouter()
const props = defineProps({
'orderId': {
type: Number,
default: 0
},
'orderStatus': {
type: String,
default: 0
}
})
const orderStatus = ref(parseInt(props.orderStatus))
const order_id = ref(props.orderId)
const order_offer = ref({})
const buy_list = ref({})
const supplier_list = ref([])
const seller_list = ref([])
const sellerInfo = ref([])
onMounted(async () => {
const {data:offer} = await getUserOrder(order_id.value)
order_offer.value = offer.data
if(offer.data.goods_array) {
offer.data.goods_array.forEach(item => {
let {brand,brand_name,code,amount,name,supplier,urgency} = item
if(`${supplier}`.startsWith('G')) {
let arr = supplier.split('-')
if(arr[0] in buy_list.value) {
buy_list.value[arr[0]].push({id: arr[1],brand,brand_name,code,name,amount,price:item.cost_price,urgency,type:'seller'})
} else {
buy_list.value[arr[0]] = [{id: arr[1],brand,brand_name,code,name,amount,price:item.cost_price,urgency,type:'seller'}]
}
} else if(supplier != 0) {
if(supplier in buy_list.value) {
buy_list.value[supplier].push({id: 0,brand,brand_name,code,name,amount,price:item.cost_price,urgency,type:'supplier'})
} else {
buy_list.value[supplier] = [{id: 0,brand,brand_name,code,name,amount,price:item.cost_price,urgency,type:'supplier'}]
}
}
})
}
const {data:supplier} = await Suppliers(offer.data.suppliers)
supplier_list.value = supplier.data.map(item => ({id:item.sid,name:item.sname,mobile:item.mobile}))
const {data:seller} = await shopList(offer.data.shops)
seller_list.value = seller.data.map(item => ({id:`G${item.id}`,name:`*商城*${item.seller_name}`,mobile:item.mobile}))
sellerInfo.value = seller.data
})
const supplierName = computed(() => (id) => {
if(`${id}`.startsWith('G')) {
let ids = `${id}`.split('-')
let idx = seller_list.value.findIndex(item => item.id == ids[0])
if(idx != -1) {
return seller_list.value[idx].name
}
} else {
let idx = supplier_list.value.findIndex(item => item.id == id)
if(idx != -1) {
return supplier_list.value[idx].name
}
}
return 'unknown'
})
const sellerName = (id) => {
if(`${id}`.startsWith('G')) {
let ids = `${id}`.split('-')
let idx = seller_list.value.findIndex(item => item.id == ids[0])
if(idx != -1) {
return seller_list.value[idx].name
}
} else {
let idx = supplier_list.value.findIndex(item => item.id == id)
if(idx != -1) {
return supplier_list.value[idx].name
}
}
return 'unknown'
}
const getSummaries = (param) => {
const { columns, data } = param
const sums = []
columns.forEach((column, index) => {
if (index === 1) {
sums[index] = '合计金额'
return
}
if (index === 7) {
let price_sum = 0
data.forEach(item => price_sum += parseFloat(item.amount) * parseFloat(item.sell_price))
sums[index] = `${price_sum.toFixed(2)}`
return
}
})
return sums
}
const orderSummaries = (param) => {
const { columns, data } = param
const sums = []
columns.forEach((column, index) => {
if (index === 1) {
sums[index] = '合计金额'
return
}
if (index === 6) {
let price_sum = 0
data.forEach(item => price_sum += parseFloat(item.amount) * parseFloat(item.price))
sums[index] = `${price_sum.toFixed(2)}`
return
}
})
return sums
}
const downloadOrderFile = () => {
var time = new Date();
var ext = `采购订单-${time.getFullYear()}${time.getMonth() + 1}${time.getDate()}-${Math.round(Math.random() * 10000)}`;
createOrderExcel(`${ext}.xlsx`)
ElMessage({
message: '成功下载采购订单!',
type: 'success',
})
}
const createOrderExcel = (fn) => {
const workbook = XLSX.utils.book_new();
const align_center = {
horizontal: 'center',
vertical: 'center',
wrapText: true
}
const align_right = {
horizontal: 'right',
vertical: 'center',
wrapText: true
}
const title_style = {
font: { bold: true },
alignment: align_center
};
const style_center = {
alignment: align_center
}
const style_right = {
alignment: align_right
}
const dataRow = 4
for(let seller in buy_list.value) {
let worksheet = XLSX.utils.aoa_to_sheet([[`${sellerName(seller)}采购清单`]], {origin: 'A1'})
if(!worksheet["!rows"]) {
worksheet["!rows"] = []
}
if(!worksheet["!rows"][0]) {
worksheet["!rows"][0] = {hpx: 22}
}
if(!worksheet["!cols"]) {
worksheet["!cols"] = []
}
worksheet["!cols"][2] = {wch: 20}
worksheet['A1']['s'] = {font:{bold:true,sz:14},alignment:{horizontal:'center',wrapText:true}}
worksheet['!merges'] = [{s:{r:0,c:0},e:{r:0,c:5}}]
worksheet['!merges'].push({s:{r:1,c:0},e:{r:1,c:5}})
worksheet['A2'] = {t:'d',v:order_offer.value.update_time}
worksheet['A2'].z = '制表日期YYYY年MM月DD日'
XLSX.utils.sheet_add_aoa(worksheet, [['品牌', '编码', '品名','采购单价(元)', '采购数量','小计']], {origin: "A3"})
let data = buy_list.value[seller]
worksheet['A3'].s = title_style
worksheet['B3'].s = title_style
worksheet['C3'].s = title_style
worksheet['D3'].s = title_style
worksheet['E3'].s = title_style
worksheet['F3'].s = title_style
data.forEach((v,row) => {
XLSX.utils.sheet_add_aoa(worksheet, [[v.brand_name,v.code,v.name,v.price, v.amount,{t:'n',v:0,f:`D${row+dataRow}*E${row+dataRow}`}]], {origin: `A${row + dataRow}`})
worksheet[`A${row + dataRow}`].s = style_center
worksheet[`D${row + dataRow}`].s = style_right
worksheet[`E${row + dataRow}`].s = style_right
worksheet[`D${row + dataRow}`].z = '0.00'
worksheet[`E${row + dataRow}`].z = '0'
worksheet[`F${row + dataRow}`].z = '0.00'
})
let row = data.length + dataRow - 2
XLSX.utils.sheet_add_aoa(worksheet, [['合计',{t:'n',v:0,f:`SUM(F${dataRow}:F${row+1})`,z:'¥0.00元'}]], {origin: -1})
worksheet[XLSX.utils.encode_cell({r:row+1, c:0})].s = title_style
worksheet['!merges'].push({s:{r:row+1,c:1},e:{r:row+1,c:5}})
XLSX.utils.book_append_sheet(workbook, worksheet);
}
XLSX.writeFile(workbook,fn,{compression: true});
}
const downloadOfferFile = () => {
var time = new Date();
var ext = `${order_offer.value.username}-${time.getFullYear()}${time.getMonth() + 1}${time.getDate()}-${Math.round(Math.random() * 10000)}`;
createExcel(`${ext}.xlsx`)
ElMessage({
message: '成功下载客户订单!',
type: 'success',
})
}
const createExcel = (fn) => {
const worksheet = XLSX.utils.aoa_to_sheet([[`${order_offer.value.seller_name}报价单`]], {origin: 'A1'})
if(!worksheet["!rows"]) {
worksheet["!rows"] = []
}
if(!worksheet["!rows"][0]) {
worksheet["!rows"][0] = {hpx: 22}
}
if(!worksheet["!cols"]) {
worksheet["!cols"] = []
}
worksheet["!cols"][2] = {wch: 20}
worksheet['A1']['s'] = {font:{bold:true,sz:14},alignment:{horizontal:'center',wrapText:true}}
worksheet['!merges'] = [{s:{r:0,c:0},e:{r:0,c:7}}]
worksheet['!merges'].push({s:{r:1,c:0},e:{r:1,c:7}})
worksheet['A2'] = {t:'d',v:order_offer.value.update_time}
worksheet['A2'].z = '报价日期YYYY年MM月DD日'
XLSX.utils.sheet_add_aoa(worksheet, [['品牌', '编码', '品名','面价(元)', '售价(元)','数量','小计','备注']], {origin: "A3"})
let data = order_offer.value.goods_array
const dataRow = 4
const align_center = {
horizontal: 'center',
vertical: 'center',
wrapText: true
}
const align_right = {
horizontal: 'right',
vertical: 'center',
wrapText: true
}
const title_style = {
font: { bold: true },
alignment: align_center
};
const style_center = {
alignment: align_center
}
const style_right = {
alignment: align_right
}
worksheet['A3'].s = title_style
worksheet['B3'].s = title_style
worksheet['C3'].s = title_style
worksheet['D3'].s = title_style
worksheet['E3'].s = title_style
worksheet['F3'].s = title_style
worksheet['G3'].s = title_style
worksheet['H3'].s = title_style
data.forEach((v,row) => {
XLSX.utils.sheet_add_aoa(worksheet, [[v.brand_name,v.code,v.name,v.market_price, v.sell_price,v.amount,{t:'n',v:0,f:`E${row+dataRow}*F${row+dataRow}`},v.note??'']], {origin: `A${row + dataRow}`})
worksheet[`A${row + dataRow}`].s = style_center
worksheet[`D${row + dataRow}`].s = style_right
worksheet[`E${row + dataRow}`].s = style_right
worksheet[`F${row + dataRow}`].s = style_right
worksheet[`G${row + dataRow}`].s = style_right
worksheet[`D${row + dataRow}`].z = '0.00'
worksheet[`E${row + dataRow}`].z = '0.00'
worksheet[`F${row + dataRow}`].z = '0'
worksheet[`G${row + dataRow}`].z = '0.00'
})
let row = data.length + dataRow - 2
XLSX.utils.sheet_add_aoa(worksheet, [['合计',{t:'n',v:0,f:`SUM(G${dataRow}:G${row+1})`,z:'¥0.00元'}]], {origin: -1})
worksheet[XLSX.utils.encode_cell({r:row+1, c:0})].s = title_style
worksheet['!merges'].push({s:{r:row+1,c:1},e:{r:row+1,c:7}})
const workbook = XLSX.utils.book_new();
if(!workbook.Custprops) {
workbook.Custprops = {}
}
workbook.Custprops["link"] = order_offer.value.link;
workbook.Custprops["order_no"] = order_offer.value.order_no;
XLSX.utils.book_append_sheet(workbook, worksheet, `客户${order_offer.value.username}订单`);
XLSX.writeFile(workbook,fn,{compression: true});
}
</script>

View File

@ -0,0 +1,91 @@
<template>
<el-autocomplete
v-model="code"
:fetch-suggestions="search"
@select="handleSelect"
@input="handleInput"
clearable
class="inline-input w-50"
placeholder="件号关键字"
:style="{width: props.width + 'px','font-size': props.font + 'px'}"
/>
</template>
<script setup>
import { ref,watch } from 'vue'
import { searchProduct } from '@/api'
import _ from 'lodash'
const props = defineProps({
modelValue: {
type: String,
default: ''
},
width: {
type: Number,
default: 120
},
font: {
type: Number,
default: 12
}
})
const emits = defineEmits(['update:modelValue','selected'])
const code = ref(props.modelValue)
watch(() => props.modelValue, (newValue) => {
if(newValue == '') {
code.value = ''
}
})
const search = (qs,cb) => _.debounce(search_list(qs,cb),500)
const search_list = async (qs, cb) => {
if(qs && qs.length >= 3) {
try {
const {data:slist} = await searchProduct(qs)
var list = slist.data
if (list.length == 0) {
list.push({
id: '-1',
value: '无匹配结果'
})
cb(list);
} else {
list = list.map(item => ({
value:`${item.brand} ${item.code}`,
id: `${item.id}`,
code: `${item.code}`,
brand: `${item.brand}`
})
)
cb(list);
}
} catch (error) {
console.log(error)
}
} else {
cb([{
value: '请输入至少3个字符',
id: -1,
}])
}
}
const handleSelect = (item) => {
if(item.id != -1) {
emits('selected',{
id: item.id,
code: item.code,
})
code.value = item.code
} else {
emits('selected',{
id: 0, code: code.value
})
}
}
const handleInput = (val) => {
emits('update:modelValue',val)
}
</script>

326
src/components/Result.vue Normal file
View File

@ -0,0 +1,326 @@
<template>
<el-tabs type="border-card">
<el-tab-pane>
<template #label>
<span style="font-weight: 600;">客户报价单</span>
</template>
<div style="margin-top: 20px;text-align: left;" v-if="order_id || (order_offer.goods_array && order_offer.goods_array.length)">
<p>客户名{{order_offer.true_name ? order_offer.true_name : order_offer.username}}</p>
<p>订单号{{order_offer.order_no}}</p>
<p>创建日期{{order_offer.create_time}}</p>
<p>报价日期{{order_offer.update_time}}</p>
<p>收货地址{{order_offer.addr_id != 0 ? `${order_offer.area_name} ${order_offer.address} ${order_offer.accept_name }(${order_offer.mobile})` : '无'}}</p>
<hr>
<div style="margin-top: 20px;text-align: right;width:100%;">
<el-button type="warning" :icon="Document" @click="downloadOfferFile()">下载Excel报价单</el-button>
<el-button type="warning" :icon="Link" @click="copyToClipboard(order_offer.link)">发送在线报价单链接</el-button>
</div>
<el-table
:data="order_offer.goods_array"
fit
style="margin-top: 20px;"
:summary-method="getSummaries"
show-summary
>
<el-table-column fixed label="#" width="50px" align="center" halign="center">
<template #default="scope">
{{ scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="code" label="产品编码"/>
<el-table-column prop="brand_name" label="品牌" />
<el-table-column prop="name" label="名称" width="160px" :show-overflow-tooltip="true"/>
<el-table-column prop="market_price" label="面价单价(元)" align="right">
<template #default="{row}">
{{ parseFloat(row.market_price).toFixed(2) }}
</template>
</el-table-column>
<el-table-column prop="sell_price" label="销售单价(元)" align="right">
<template #default="{row}">
{{ parseFloat(row.sell_price).toFixed(2) }}
</template>
</el-table-column>
<el-table-column prop="sell_price2" label="急件单价(元)" align="right">
<template #default="{row}">
{{ row.sell_price2 ? parseFloat(row.sell_price2).toFixed(2) : 0.00 }}
</template>
</el-table-column>
<el-table-column prop="amount" label="数量(件/套)" align="right"/>
<!-- <el-table-column prop="store" label="库存(件/套)" align="right"/> -->
<el-table-column label="销售总价(元)" align="right">
<template #default="{row}">
{{ (parseFloat(row.sell_price)*parseInt(row.amount)).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="备注">
<template #default="{row}">
{{ row.note ?? '' }}
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
<el-tab-pane>
<template #label>
<span style="font-weight: 600;">预采购订单</span>
</template>
<div style="margin-top: 20px;" v-for="(item,i) in buy_list" :key="i">
<h3>{{ supplierName(i) }}采购清单</h3>
<el-table
:data="item"
fit
:summary-method="orderSummaries"
show-summary
>
<el-table-column fixed label="#" width="50px" align="center" halign="center">
<template #default="scope">
{{ scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="code" label="产品编码"/>
<el-table-column prop="brand_name" label="商品品牌"/>
<el-table-column prop="name" label="商品名称" width="160px" :show-overflow-tooltip="true" />
<el-table-column prop="price" label="采购单价" align="right">
<template #default="{row}">
{{ parseFloat(row.price).toFixed(2) }}
</template>
</el-table-column>
<el-table-column prop="price2" label="急件单价" align="right">
<template #default="{row}">
{{ row.price2 ? parseFloat(row.price2).toFixed(2) : '0.00' }}
</template>
</el-table-column>
<el-table-column prop="amount" label="采购数量" align="right"/>
<el-table-column label="单项总价" align="right">
<template #default="{row}">
{{ (parseFloat(row.price)*parseInt(row.amount)).toFixed(2) }}
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
</el-tabs>
</template>
<script setup>
import { ref,computed,onMounted } from 'vue'
import { useRoute,useRouter } from 'vue-router'
import { $getOrderOffer,$actionLog,Suppliers,shopList } from '@/api'
import { Document,Link } from '@element-plus/icons-vue'
import XLSX from 'xlsx-js-style'
import { useUserStore } from '@/store'
import { ElMessage } from 'element-plus'
const emit = defineEmits(['next-step','add-goods','make-order'])
const user_store = useUserStore()
const route = useRoute()
const router = useRouter()
const props = defineProps({
'orderId': {
type: Number,
default: 0
},
'orderStatus': {
type: String,
default: 0
}
})
const orderStatus = ref(parseInt(props.orderStatus))
const order_id = ref(0)
const order_offer = ref({})
const buy_list = ref({})
const supplier_list = ref([])
const seller_list = ref([])
const sellerInfo = ref([])
onMounted(async ()=>{
order_id.value = props.orderId
const {data:offer} = await $getOrderOffer(order_id.value)
order_offer.value = offer.data
if(offer.data.goods_array) {
offer.data.goods_array.forEach(item => {
let {brand,brand_name,code,amount,name,selected} = item
if(`${selected}`.startsWith('G')) {
let arr = selected.split('-')
if(arr[0] in buy_list.value) {
buy_list.value[arr[0]].push({id: arr[1],brand,brand_name,code,name,amount,price:item.from[0],price2:item.from[3] ?? 0.00,type:'seller'})
} else {
buy_list.value[arr[0]] = [{id: arr[1],brand,brand_name,code,name,amount,price:item.from[0],price2:item.from[3] ?? 0.00,type:'seller'}]
}
} else if(selected != 0) {
if(selected in buy_list.value) {
buy_list.value[selected].push({id: 0,brand,brand_name,code,name,amount,price:item.from[0],price2:item.from[3] ?? 0.00,type:'supplier'})
} else {
buy_list.value[selected] = [{id: 0,brand,brand_name,code,name,amount,price:item.from[0],price2:item.from[3] ?? 0.00,type:'supplier'}]
}
}
})
}
const {data:supplier} = await Suppliers(offer.data.suppliers)
supplier_list.value = supplier.data.map(item => ({id:item.sid,name:item.sname,mobile:item.mobile}))
const {data:seller} = await shopList(offer.data.shops)
seller_list.value = seller.data.map(item => ({id:`G${item.id}`,name:`*商城*${item.seller_name}`,mobile:item.mobile}))
sellerInfo.value = seller.data
})
const supplierName = computed(() => (id) => {
if(`${id}`.startsWith('G')) {
let ids = `${id}`.split('-')
let idx = seller_list.value.findIndex(item => item.id == ids[0])
if(idx != -1) {
return seller_list.value[idx].name
}
} else {
let idx = supplier_list.value.findIndex(item => item.id == id)
if(idx != -1) {
return supplier_list.value[idx].name
}
}
return 'unknown'
})
const getSummaries = (param) => {
const { columns, data } = param
const sums = []
columns.forEach((column, index) => {
if (index === 1) {
sums[index] = '合计金额'
return
}
if (index === 8) {
let price_sum = 0
data.forEach(item => price_sum += parseFloat(item.amount) * parseFloat(item.sell_price))
sums[index] = `${price_sum.toFixed(2)}`
return
}
})
return sums
}
const orderSummaries = (param) => {
const { columns, data } = param
const sums = []
columns.forEach((column, index) => {
if (index === 1) {
sums[index] = '合计金额'
return
}
if (index === 7) {
let price_sum = 0
data.forEach(item => price_sum += parseFloat(item.amount) * parseFloat(item.price))
sums[index] = `${price_sum.toFixed(2)}`
return
}
})
return sums
}
const copyToClipboard = async (content) => {
if (window.clipboardData) {
window.clipboardData.setData('text', content);
} else {
(function (content) {
document.oncopy = function (e) {
e.clipboardData.setData('text', content);
e.preventDefault();
document.oncopy = null;
}
})(content);
document.execCommand('Copy');
}
let action = '发送报价单链接'
await $actionLog(order_id.value,user_store.name,action,'')
ElMessage({
message: `成功${action}!`,
type: 'success',
})
}
const downloadOfferFile = async () => {
var time = new Date();
var ext = `${order_offer.value.username}-${time.getFullYear()}${time.getMonth() + 1}${time.getDate()}-${Math.round(Math.random() * 10000)}`;
createExcel(`${ext}.xlsx`)
let action = '导出报价单'
await $actionLog(order_id.value,user_store.name,action,'')
ElMessage({
message: `成功${action}!`,
type: 'success',
})
}
const createExcel = (fn) => {
const worksheet = XLSX.utils.aoa_to_sheet([[`${order_offer.value.seller_name}报价单`]], {origin: 'A1'})
if(!worksheet["!rows"]) {
worksheet["!rows"] = []
}
if(!worksheet["!rows"][0]) {
worksheet["!rows"][0] = {hpx: 22}
}
if(!worksheet["!cols"]) {
worksheet["!cols"] = []
}
worksheet["!cols"][2] = {wch: 20}
worksheet['A1']['s'] = {font:{bold:true,sz:14},alignment:{horizontal:'center',wrapText:true}}
worksheet['!merges'] = [{s:{r:0,c:0},e:{r:0,c:8}}]
worksheet['!merges'].push({s:{r:1,c:0},e:{r:1,c:8}})
worksheet['A2'] = {t:'d',v:order_offer.value.update_time}
worksheet['A2'].z = '报价日期YYYY年MM月DD日'
XLSX.utils.sheet_add_aoa(worksheet, [['品牌', '编码', '品名','面价(元)', '售价(元)','急件单价(元)','数量','小计','备注']], {origin: "A3"})
let data = order_offer.value.goods_array
const dataRow = 4
const align_center = {
horizontal: 'center',
vertical: 'center',
wrapText: true
}
const align_right = {
horizontal: 'right',
vertical: 'center',
wrapText: true
}
const title_style = {
font: { bold: true },
alignment: align_center
};
const style_center = {
alignment: align_center
}
const style_right = {
alignment: align_right
}
worksheet['A3'].s = title_style
worksheet['B3'].s = title_style
worksheet['C3'].s = title_style
worksheet['D3'].s = title_style
worksheet['E3'].s = title_style
worksheet['F3'].s = title_style
worksheet['G3'].s = title_style
worksheet['H3'].s = title_style
worksheet['I3'].s = title_style
data.forEach((v,row) => {
XLSX.utils.sheet_add_aoa(worksheet, [[v.brand_name,v.code,v.name,v.market_price, v.sell_price,v.sell_price2 ?? 0,v.amount,{t:'n',v:0,f:`E${row+dataRow}*G${row+dataRow}`},v.note??'']], {origin: `A${row + dataRow}`})
worksheet[`A${row + dataRow}`].s = style_center
worksheet[`D${row + dataRow}`].s = style_right
worksheet[`E${row + dataRow}`].s = style_right
worksheet[`F${row + dataRow}`].s = style_right
worksheet[`G${row + dataRow}`].s = style_right
worksheet[`D${row + dataRow}`].z = '0.00'
worksheet[`E${row + dataRow}`].z = '0.00'
worksheet[`F${row + dataRow}`].z = '0.00'
worksheet[`G${row + dataRow}`].z = '0'
worksheet[`H${row + dataRow}`].z = '0.00'
})
let row = data.length + dataRow - 2
XLSX.utils.sheet_add_aoa(worksheet, [['合计',{t:'n',v:0,f:`SUM(H${dataRow}:H${row+1})`,z:'¥0.00元'}]], {origin: -1})
worksheet[XLSX.utils.encode_cell({r:row+1, c:0})].s = title_style
worksheet['!merges'].push({s:{r:row+1,c:1},e:{r:row+1,c:7}})
const workbook = XLSX.utils.book_new();
if(!workbook.Custprops) {
workbook.Custprops = {}
}
workbook.Custprops["link"] = order_offer.value.link;
workbook.Custprops["order_no"] = order_offer.value.order_no;
XLSX.utils.book_append_sheet(workbook, worksheet, `客户${order_offer.value.true_name ? order_offer.value.true_name : order_offer.value.username}报价单`);
XLSX.writeFile(workbook,fn,{compression: true});
}
</script>

View File

@ -0,0 +1,12 @@
<template>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1712162207202" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="52795" xmlns:xlink="http://www.w3.org/1999/xlink" width="60" height="60">
<path d="M512.001824 415.426129c53.231847 0 96.538598 43.30675 96.538598 96.538598s-43.30675 96.538598-96.538598 96.538598-96.538598-43.30675-96.538597-96.538598 43.30675-96.538598 96.538597-96.538598m0-44.821057c-78.071349 0-141.359655 63.289522-141.359655 141.359655s63.289522 141.359655 141.359655 141.359655 141.359655-63.289522 141.359656-141.359655-63.288306-141.359655-141.359656-141.359655z" fill="#4E4D4D" p-id="52796"></path><path d="M512.001824 143.049111c49.823744 0 98.131965 9.747516 143.585505 28.972526 43.924636 18.579149 83.382979 45.184734 117.277915 79.080887 33.894936 33.894936 60.501738 73.353279 79.080887 117.277915 19.22501 45.452323 28.972526 93.76176 28.972526 143.585504s-9.747516 98.131965-28.972526 143.585504c-18.579149 43.924636-45.184734 83.382979-79.080887 117.277916-33.894936 33.894936-73.353279 60.501738-117.277915 79.080887-45.452323 19.22501-93.76176 28.972526-143.585505 28.972526s-98.131965-9.747516-143.585504-28.972526c-43.924636-18.579149-83.382979-45.184734-117.277915-79.080887-33.894936-33.894936-60.501738-73.353279-79.080887-117.277916-19.22501-45.452323-28.972526-93.76176-28.972526-143.585504s9.747516-98.131965 28.972526-143.585504c18.579149-43.924636 45.184734-83.382979 79.080887-117.277915s73.353279-60.501738 117.277915-79.080887c45.453539-19.22501 93.76176-28.972526 143.585504-28.972526m0-44.821057c-228.501035 0-413.736673 185.236855-413.736673 413.736673s185.236855 413.736673 413.736673 413.736673S925.738498 740.465762 925.738498 511.964727 740.502859 98.228054 512.001824 98.228054z" fill="#4E4D4D" p-id="52797"></path><path d="M512.001824 182.96357c7.53383 0 15.001979 0.277319 22.410529 0.77479V53.340099h-44.821057v130.398261c7.407333-0.497471 14.875483-0.77479 22.410528-0.77479zM534.412353 243.770602a299.998223 299.998223 0 0 0-44.821057 0v245.784813H243.806483a299.998223 299.998223 0 0 0 0 44.821057h245.784813v233.621704a299.998223 299.998223 0 0 0 44.821057 0V534.375256h245.783597a299.998223 299.998223 0 0 0 0-44.821058H534.412353V243.770602zM840.226975 489.554198c0.498687 7.407333 0.77479 14.876699 0.77479 22.410529s-0.277319 15.001979-0.77479 22.410529H970.625236v-44.821058H840.226975zM183.000668 511.964727c0-7.53383 0.277319-15.001979 0.776006-22.410529H53.377197v44.821058H183.776674c-0.498687-7.407333-0.776006-14.876699-0.776006-22.410529zM512.001824 840.965884c-7.53383 0-15.001979-0.277319-22.410528-0.77479v130.398261h44.821057V840.191094c-7.40855 0.497471-14.876699 0.77479-22.410529 0.77479z" fill="#4E4D4D" p-id="52798"></path>
</svg>
</template>
<script setup>
</script>
<style lang='scss'>
</style>

View File

@ -0,0 +1,12 @@
<template>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1712159182410" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6186" xmlns:xlink="http://www.w3.org/1999/xlink" width="60" height="60">
<path d="M536.677 215.067l0.338 0.398 0.336 0.408 310.035 383.19a16 16 0 0 1 3.561 10.063c0 8.732-6.994 15.83-15.685 15.997l-0.315 0.003H660.473v256c0 17.673-14.326 32-32 32h-232c-17.672 0-32-14.327-32-32v-256H190.001a16 16 0 0 1-9.745-3.31l-0.32-0.251c-6.776-5.483-7.902-15.362-2.596-22.222l0.222-0.28 310.035-383.19a32 32 0 0 1 4.75-4.749c13.465-10.895 33.088-9.07 44.33 3.943z m-24.203 84.527L307.343 553.125h129.13v288.001h152v-288h129.13l-205.13-253.532zM855.99 112c4.45 0 8.009 3.41 8.009 7.579v56.842c0 4.168-3.56 7.579-8.009 7.579H168.009c-4.45 0-8.009-3.41-8.009-7.579V119.58c0-4.168 3.56-7.579 8.009-7.579z" p-id="6187"></path>
</svg>
</template>
<script setup>
</script>
<style lang='scss'>
</style>

View File

@ -68,6 +68,9 @@ export const useUserStore = defineStore('user-info',{
userId: (state) => {
return state.userInfo.id || ''
},
userName: (state) => {
return state.userInfo.name || ''
},
isAdmin: (state) => {
return state.userInfo.role === '0' || state.userInfo.role === '1'
},

View File

@ -1,8 +1,16 @@
export const adminRoutes = [
{
path:'/inquiry-list',
name:'InquiryList',
path:'/inquiry/list',
name:'inquiry-list',
component: 'inquiry/Index'
},{
path:'/inquiry/create',
name:'inquiry-create',
component: 'inquiry/edit'
},{
path:'/inquiry/edit/:id',
name:'inquiry-edit',
component: 'inquiry/edit'
},{
path:'/customer-list',
name:'CustomerList',
@ -27,11 +35,11 @@ export const adminMenus = [
{
icon: 'fa fa-list',
name: '询盘列表',
path: '/inquiry-list'
path: '/inquiry/list'
},{
icon: 'fa fa-plus',
name: '新增询价',
path: '/inquiry-create'
path: '/inquiry/create'
}
]
},{

View File

@ -59,12 +59,11 @@
</div>
</el-col>
<el-col :span="3" :offset="1">
<el-button @click="" type="danger"><i class="fa fa-plus pr-2"></i>手动建单</el-button>
<el-button @click="router.push({path:'/inquiry/create'})" type="danger"><i class="fa fa-plus pr-2"></i>手动建单</el-button>
<el-button color="#00828a" @click="handleMerge" :disabled="selectionRows.length<2" class="ml-2"><i class="fa fa-chain"></i>合并订单</el-button>
</el-col>
</el-row>
<!-- @row-click="expandPanel" -->
<el-table
ref="orderTableRef"
:data="order_list"
@ -169,22 +168,6 @@
</template>
</el-table-column>
</el-table-column>
<!--
<el-table-column label="客户销售" header-align="center">
<el-table-column prop="sell_price" label="售价" show-overflow-tooltip width="100" align="right" header-align="center">
<template #default="{row}">
{{ (Math.round(row.sell_price * 100) / 100.00).toFixed(2) }}
</template>
</el-table-column>
<el-table-column prop="sell_price2" label="急件价" show-overflow-tooltip width="100" align="right" header-align="center">
<template #default="{row}">
{{ (Math.round(row.sell_price2 * 100) / 100.00).toFixed(2) }}
</template>
</el-table-column>
<el-table-column prop="amount" label="销售数" width="60" align="right" header-align="center"/>
<el-table-column prop="note" label="备注" show-overflow-tooltip width="80" header-align="center"/>
</el-table-column> -->
</el-table>
</div>
</template>
@ -197,7 +180,7 @@
</el-table-column>
<el-table-column prop="order_no" label="尾号" header-align="center" align="center" width="86px">
<template #default="{row}">
<el-link type="primary" underline="never" @click="router.push({path:'/home',query:{id:row.id}})"><el-text size="small" tag="b" class="hover:(bg-transparent text-red-900)">{{ row.order_no.slice(12) }}</el-text></el-link>
<el-link type="primary" underline="never" @click="router.push({path:`/inquiry/edit/${row.id}`})"><el-text size="small" tag="b" class="hover:(bg-transparent text-red-900)">{{ row.order_no.slice(12) }}</el-text></el-link>
</template>
</el-table-column>
<el-table-column label="客户名称" :show-overflow-tooltip="true" width="180px" header-align="center">
@ -240,28 +223,6 @@
</el-tag>
</template>
</el-table-column>
<!-- <el-table-column label="订单商品" align="center" header-align="center">
<template #default="{row}">
<el-popover effect="light" trigger="hover" placement="left-start" width="auto">
<template #default>
<el-table
fit
:data="goodsSorted(row.goods)"
:height="240"
style="width:320px"
>
<el-table-column prop="code" label="编码" :show-overflow-tooltip="true" width="100px"/>
<el-table-column prop="b_name" label="品牌" :show-overflow-tooltip="true" width="80px" />
<el-table-column prop="name" label="品名" :show-overflow-tooltip="true" width="80px" />
<el-table-column prop="amount" label="数量" width="60px" />
</el-table>
</template>
<template #reference>
<el-tag color="#00828a" size="large" effect="dark" round>{{row.offer.length}}/{{row.goods.length}}</el-tag>
</template>
</el-popover>
</template>
</el-table-column> -->
<el-table-column label="询盘报价" align="right" header-align="center" prop="total_price" width="160">
<template #default="{row}">
<el-text :type="row.total_price==0?'danger':'primary'">{{`${row.total_price.toFixed(2)}`}}</el-text>
@ -294,7 +255,7 @@
@update:page-size="handleSizeChange"
/>
</div>
<!--
<el-dialog
v-model="workflowDialogVisible"
title="订单工作流"
@ -325,7 +286,7 @@
<el-button type="primary" @click="workflowDialogVisible=false" :icon="Share">知道了!</el-button>
</span>
</template>
</el-dialog> -->
</el-dialog>
</template>
<script setup>
@ -333,6 +294,7 @@
import { useRouter } from 'vue-router'
import dayjs from 'dayjs'
import { ElMessage } from 'element-plus'
import { addLog,getLog } from '@/api/user'
import { inquiryList,customerList,inquiryStatus,createInquiry,addInquiryGoods,removeInquiry,supplierList,sellerList } from '@/api/inquiry'
import { status_name } from '@/utils'
import { storeToRefs } from 'pinia'
@ -395,7 +357,7 @@
const workflow = ref([])
const handleWorkflow = async (row) => {
workflow.value = []
const {data:res} = await getActionLog(row.id)
const {data:res} = await getLog(row.id)
workflow.value = res.data
workflowDialogVisible.value = true
}
@ -501,7 +463,7 @@
await addInquiryGoods(res.data.order_id,goods)
let orders = rows.map(order => order.id)
await removeInquiry(orders)
// await $actionLog(res.data.order_id,user_store.name,'',JSON.stringify(orders))
await addLog(res.data.order_id,`${userStore.userName}(${userStore.userId})`,'合并订单',JSON.stringify(orders))
ElMessage({
message: '订单合并成功!',
type: 'success',
@ -513,15 +475,6 @@
</script>
<style scoped>
/* .el-table::before {
height: 0px;
}
.el-table--border::after {
width: 0px;
}
.el-table--border {
border: none;
} */
.el-table .cell {
white-space: pre-wrap;
}

230
src/views/inquiry/edit.vue Normal file
View File

@ -0,0 +1,230 @@
<script setup>
import { ref,onMounted,reactive,defineAsyncComponent,markRaw,shallowRef } from 'vue'
import { useRoute,useRouter } from 'vue-router'
import { inquiryInfo,inquiryGoodsList } from '@/api/inquiry'
import { HomeFilled,Search,EditPen,Flag,ShoppingCartFull } from '@element-plus/icons-vue'
const route = useRoute()
const router = useRouter()
const order_id = ref(0)
const order = ref({})
const status = ref(0)
const goods_list = ref([])
const currentPage = ref('order')
const startShow = ref(false)
const load = async () => {
order_id.value = route.params.id ? +route.params.id : 0
currentPage.value = 'order'
if(order_id.value != 0) {
const {data:res} = await inquiryInfo(order_id.value)
if(res.data.id && parseInt(res.data.id) == order_id.value) {
order.value = res.data
status.value = +order.value.status
const { data:goods } = await inquiryGoodsList(order_id.value)
goods_list.value = goods.data
}
changeStatus(status.value)
}
startShow.value = true
}
onMounted(async () => {
await load()
comId.value = tabs['order'].component
})
const OrderBase = defineAsyncComponent(() => import('@/components/OrderBase.vue'))
const Inquiry = defineAsyncComponent(() => import('@/components/Inquiry.vue'))
const comId = shallowRef(OrderBase)
const tabs = reactive({
order: {
label: '1.订单基础',
icon: markRaw(HomeFilled),
disabled: false,
component: markRaw(OrderBase)
},
inquiry: {
label: '2.询货查价',
icon: markRaw(Search),
disabled: true,
component: markRaw(Inquiry)
},
})
const changeStatus = (status) => {
switch(status) {
case 0:
// tabs.result.disabled = true
if(goods_list.value.length == 0) {
tabs.inquiry.disabled = true
// tabs.offer.disabled = true
} else {
tabs.inquiry.disabled = false
// tabs.offer.disabled = false
}
// tabs.order_result.disabled = true
break
case 1:
// tabs.result.disabled = true
tabs.inquiry.disabled = false
// tabs.offer.disabled = false
// tabs.order_result.disabled = true
break
case 2:
case 3:
tabs.inquiry.disabled = false
// tabs.offer.disabled = false
// tabs.result.disabled = false
// tabs.order_result.disabled = true
break
case 5:
tabs.inquiry.disabled = true
// tabs.offer.disabled = true
// tabs.result.disabled = true
// tabs.order_result.disabled = false
break
default:
tabs.inquiry.disabled = true
// tabs.offer.disabled = true
// tabs.result.disabled = true
// tabs.order_result.disabled = true
break
}
}
const handleChange = (tab,evt) => {
comId.value = tabs[tab.props.name].component
}
const handleStatusChange = async (id) => {
if(!id) {
return
}
const { data:res } = await inquiryInfo(id)
let status = 0
let oldId = order_id.value
if(res.data.id && parseInt(res.data.id) == id) {
order_id.value = id
order.value = res.data
status = parseInt(order.value.status)
}
changeStatus(status)
// if(parseInt(oldId) == 0) {
// router.push({
// path: '/pre-order/edit',
// query: {
// id
// }
// })
// }
}
/*
const Offer = defineAsyncComponent(() => import('@/components/Offer.vue'))
const Result = defineAsyncComponent(() => import('@/components/Result.vue'))
const OrderResult = defineAsyncComponent(() => import('@/components/OrderResult.vue'))
const tabs = reactive({
order: {
label: '1.订单基础',
icon: markRaw(HomeFilled),
disabled: false,
component: markRaw(OrderBase)
},
inquiry: {
label: '2.询货查价',
icon: markRaw(Search),
disabled: true,
component: markRaw(Inquiry)
},
offer: {
label: '3.优选报价',
icon: markRaw(EditPen),
disabled: true,
component: markRaw(Offer)
},
result: {
label: '4.询盘结果',
icon: markRaw(Flag),
disabled: true,
component: markRaw(Result)
},
order_result: {
label: '5.客户订单',
icon: markRaw(ShoppingCartFull),
disabled: true,
component: markRaw(OrderResult)
}
})
const saveGoods = async (id) => {
const {data:res} = await inquiryGoodsList(id)
goods_list.value = res.data
if(goods_list.value.length > 0) {
tabs.inquiry.disabled = false
} else {
tabs.inquiry.disabled = true
}
changeStatus(parseInt(order.value.status))
}
const nextStep = async (id) => {
if(!id) {
return
}
const {data:res} = await inquiryInfo(id)
let status = 0
if(res.data.id && parseInt(res.data.id) == id) {
order_id.value = id
order.value = res.data
status = parseInt(order.value.status)
}
changeStatus(status)
}
load()
*/
</script>
<template>
<el-tabs v-model="currentPage" type="border-card" @tab-click="handleChange">
<el-tab-pane v-for="(item,index) in tabs" :name="index" :key="index" :disabled="item.disabled">
<template #label>
<span class="tabs-label">
<el-icon>
<component :is="item.icon"></component>
</el-icon>
<span>{{item.label}}</span>
</span>
</template>
</el-tab-pane>
<Suspense v-if="startShow">
<component
@status-change="handleStatusChange"
:is="tabs[currentPage].component"
:order-id="order_id"
:order-status="+order.status"
/>
<template #fallback>
功能加载中请稍候...
</template>
</Suspense>
<!-- @make-order="makeOrder"
@add-goods="saveGoods"
@next-step="nextStep" -->
</el-tabs>
</template>
<style scoped>
.tabs-label .el-icon {
vertical-align: middle;
}
.tabs-label span {
vertical-align: middle;
margin-left: 10px;
font-weight: 600;
}
</style>