mirror of
https://github.com/PanJiaChen/vue-element-admin.git
synced 2025-08-10 12:01:57 +08:00
Add product information form (#551)
* Add Form The PorductInfo * remove console.log
This commit is contained in:
parent
1e40283c4e
commit
210443b395
12
src/components/ADempiere/Form/ProductInfo/fieldsList.js
Normal file
12
src/components/ADempiere/Form/ProductInfo/fieldsList.js
Normal file
@ -0,0 +1,12 @@
|
||||
export default [
|
||||
// Product Code
|
||||
{
|
||||
elementColumnName: 'ProductValue',
|
||||
columnName: 'ProductValue',
|
||||
isFromDictionary: true,
|
||||
overwriteDefinition: {
|
||||
size: 24,
|
||||
sequence: 10
|
||||
}
|
||||
}
|
||||
]
|
173
src/components/ADempiere/Form/ProductInfo/index.vue
Normal file
173
src/components/ADempiere/Form/ProductInfo/index.vue
Normal file
@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<div>
|
||||
<product-info-list />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* This component is made to be the prototype of the Product Info search field
|
||||
*/
|
||||
import ProductInfoList from './productList'
|
||||
import fieldMixin from '@/components/ADempiere/Field/mixin/mixinField.js'
|
||||
import {
|
||||
formatPrice,
|
||||
formatQuantity
|
||||
} from '@/utils/ADempiere/valueFormat.js'
|
||||
|
||||
export default {
|
||||
name: 'ProductInfo',
|
||||
components: {
|
||||
ProductInfoList
|
||||
},
|
||||
mixins: [
|
||||
fieldMixin
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
timeOut: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isShowProductsPriceList: {
|
||||
get() {
|
||||
return this.$store.state['pointOfSales/listProductPrice'].productPrice.isShowPopoverField
|
||||
},
|
||||
set(isShowed) {
|
||||
if (!this.isEmptyValue(this.$route.query.pos)) {
|
||||
this.$store.commit('showListProductPrice', {
|
||||
attribute: 'isShowPopoverField',
|
||||
isShowed
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
listWithPrice() {
|
||||
const { productPricesList } = this.$store.getters.getProductPrice
|
||||
if (!this.isEmptyValue(productPricesList)) {
|
||||
return productPricesList
|
||||
}
|
||||
return []
|
||||
},
|
||||
keyShortcuts() {
|
||||
return {
|
||||
refreshList: ['f5'],
|
||||
refreshList2: ['shift', 'f5']
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatPrice,
|
||||
formatQuantity,
|
||||
shortcutKeyMethod(event) {
|
||||
console.log(event)
|
||||
switch (event.srcKey) {
|
||||
case 'refreshList':
|
||||
case 'refreshList2':
|
||||
this.$store.dispatch('listProductPriceFromServerProductInfo', {})
|
||||
break
|
||||
}
|
||||
},
|
||||
localSearch(stringToMatch, callBack) {
|
||||
if (this.isEmptyValue(stringToMatch)) {
|
||||
// not show list
|
||||
callBack([])
|
||||
return
|
||||
}
|
||||
console.log(stringToMatch, callBack)
|
||||
|
||||
let results = this.listWithPrice
|
||||
if (stringToMatch) {
|
||||
const parsedValue = stringToMatch.toLowerCase().trim()
|
||||
|
||||
results = results.filter(rowProduct => {
|
||||
const productAttributes = rowProduct.product
|
||||
|
||||
for (const columnProductPrice in productAttributes) {
|
||||
const valueToCompare = String(productAttributes[columnProductPrice]).toLowerCase()
|
||||
|
||||
if (valueToCompare.includes(parsedValue)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// Remote search
|
||||
if (this.isEmptyValue(results) && String(stringToMatch.length > 3)) {
|
||||
clearTimeout(this.timeOut)
|
||||
const epa = this.$store.getters.getSearchProduct
|
||||
console.log({ epa })
|
||||
this.timeOut = setTimeout(() => {
|
||||
this.$store.dispatch('listProductPriceFromServerProductInfo', {
|
||||
containerUuid: 'Products-Price-List-ProductInfo',
|
||||
pageNumber: 1,
|
||||
searchValue: stringToMatch
|
||||
})
|
||||
.then(() => {
|
||||
const recordsList = this.listWithPrice
|
||||
|
||||
if (this.isEmptyValue(recordsList)) {
|
||||
this.$message({
|
||||
message: 'Sin resultados coincidentes con la busqueda',
|
||||
type: 'info',
|
||||
showClose: true
|
||||
})
|
||||
}
|
||||
|
||||
callBack(recordsList)
|
||||
})
|
||||
}, 2000)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// call callback function to return suggestions
|
||||
callBack(results)
|
||||
},
|
||||
handleSelect(elementSelected) {
|
||||
const valueProduct = elementSelected.product.value
|
||||
this.$store.dispatch('notifyActionKeyPerformed', {
|
||||
containerUuid: 'POS',
|
||||
columnName: 'ProductValue',
|
||||
// TODO: Verify with 'value' or 'searchValue' attribute
|
||||
value: valueProduct
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scope>
|
||||
.custom-field-prodcut-info {
|
||||
li {
|
||||
line-height: normal;
|
||||
padding: 15px;
|
||||
|
||||
.header {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.upc {
|
||||
color: #7e7e7e;
|
||||
padding-top: 10px;
|
||||
float: left;
|
||||
}
|
||||
.description {
|
||||
padding-top: 10px;
|
||||
float: left;
|
||||
}
|
||||
.price {
|
||||
color: #7e7e7e;
|
||||
padding-top: 10px;
|
||||
float: right;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.quantityAvailable {
|
||||
float: right;
|
||||
padding-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
269
src/components/ADempiere/Form/ProductInfo/productList.vue
Normal file
269
src/components/ADempiere/Form/ProductInfo/productList.vue
Normal file
@ -0,0 +1,269 @@
|
||||
<template>
|
||||
<el-main
|
||||
v-shortkey="shortsKey"
|
||||
>
|
||||
<el-form
|
||||
v-shortkey="shortsKey"
|
||||
label-position="top"
|
||||
label-width="10px"
|
||||
@shortkey.native="keyAction"
|
||||
@submit.native.prevent="notSubmitForm"
|
||||
>
|
||||
<field-definition
|
||||
v-for="(field) in fieldsList"
|
||||
:key="field.columnName"
|
||||
:metadata-field="field"
|
||||
/>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
ref="singleTable"
|
||||
v-loading="!productPrice.isLoaded"
|
||||
:data="listWithPrice"
|
||||
border
|
||||
fit
|
||||
height="550"
|
||||
highlight-current-row
|
||||
@row-click="findlistProductWithRow"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
<el-table-column
|
||||
prop="product.value"
|
||||
label="Codigo"
|
||||
/>
|
||||
<el-table-column
|
||||
label="Producto"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-popover trigger="click" placement="right" width="300">
|
||||
<b><i> {{ scope.row.product.name }} </i> </b>
|
||||
<el-divider />
|
||||
<p><b style="float: left"> Codigo :</b><spam style="float: right">{{ scope.row.product.value }}</spam></p><br>
|
||||
<p><b style="float: left">Precio :</b><spam style="float: right"> {{ formatPrice(scope.row.priceStandard, scope.row.currency.iSOCode) }} </spam></p><br>
|
||||
<p><b style="float: left">Tax :</b><spam style="float: right"> {{ formatPrice(getTaxAmount(scope.row.priceStandard, scope.row.taxRate.rate), scope.row.currency.iSOCode) }} </spam></p><br>
|
||||
<p><b style="float: left">Gran Total :</b><spam style="float: right"> {{ formatPrice(getTaxAmount(scope.row.priceStandard, scope.row.taxRate.rate) + scope.row.priceStandard, scope.row.currency.iSOCode) }} </spam></p><br>
|
||||
<p><b style="float: left">UPC :</b><spam style="float: right"> {{ scope.row.product.upc }} </spam></p>
|
||||
<div slot="reference" class="name-wrapper">
|
||||
{{ scope.row.product.name }}
|
||||
</div>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="Tax"
|
||||
align="right"
|
||||
width="150"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ formatPrice(getTaxAmount(scope.row.priceStandard, scope.row.taxRate.rate), scope.row.currency.iSOCode) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="Precio"
|
||||
align="right"
|
||||
width="200"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ formatPrice(scope.row.priceStandard, scope.row.currency.iSOCode) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="Gran Total"
|
||||
align="right"
|
||||
width="300"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ formatPrice(getTaxAmount(scope.row.priceStandard, scope.row.taxRate.rate) + scope.row.priceStandard, scope.row.currency.iSOCode) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<custom-pagination
|
||||
:total="productPrice.recordCount"
|
||||
:current-page="productPrice.pageNumber"
|
||||
:handle-change-page="handleChangePage"
|
||||
/>
|
||||
</el-main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import formMixin from '@/components/ADempiere/Form/formMixin.js'
|
||||
import CustomPagination from '@/components/ADempiere/Pagination'
|
||||
import fieldsListProductPrice from './fieldsList.js'
|
||||
import { formatPrice } from '@/utils/ADempiere/valueFormat.js'
|
||||
|
||||
export default {
|
||||
name: 'ProductList',
|
||||
components: {
|
||||
CustomPagination
|
||||
},
|
||||
mixins: [
|
||||
formMixin
|
||||
],
|
||||
props: {
|
||||
metadata: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
uuid: 'Products-Price-List-ProductInfo',
|
||||
containerUuid: 'Products-Price-List-ProductInfo'
|
||||
}
|
||||
}
|
||||
},
|
||||
isSelectable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
popoverName: {
|
||||
type: String,
|
||||
default: 'isShowPopoverField'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultMaxPagination: 50,
|
||||
resource: {},
|
||||
fieldsList: fieldsListProductPrice,
|
||||
isCustomForm: true,
|
||||
timeOut: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
defaultImage() {
|
||||
return require('@/image/ADempiere/pos/no-image.jpg')
|
||||
},
|
||||
isShowProductsPriceList() {
|
||||
return this.$store.state['pointOfSales/listProductPrice'].productPrice[this.attribute]
|
||||
},
|
||||
currentPoint() {
|
||||
return this.$store.getters.getCurrentPOS
|
||||
},
|
||||
productPrice() {
|
||||
return this.$store.getters.getProductPrice
|
||||
},
|
||||
listWithPrice() {
|
||||
const { productPricesList } = this.productPrice
|
||||
if (!this.isEmptyValue(productPricesList)) {
|
||||
return productPricesList
|
||||
}
|
||||
return []
|
||||
},
|
||||
shortsKey() {
|
||||
return {
|
||||
closeProductList: ['esc'],
|
||||
refreshList: ['f5']
|
||||
}
|
||||
},
|
||||
isReadyFromGetData() {
|
||||
const { isLoaded, isReload } = this.productPrice
|
||||
return (!isLoaded || isReload) // && this.isShowProductsPriceList
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isReadyFromGetData(isToLoad) {
|
||||
if (isToLoad) {
|
||||
this.loadProductsPricesList()
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch('listPointOfSalesFromServer')
|
||||
this.unsubscribe = this.subscribeChanges()
|
||||
|
||||
if (this.isReadyFromGetData) {
|
||||
this.loadProductsPricesList()
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.unsubscribe()
|
||||
},
|
||||
methods: {
|
||||
formatPrice,
|
||||
srcImage(keyValue) {
|
||||
if (this.isEmptyValue(keyValue)) {
|
||||
return this.defaultImage
|
||||
}
|
||||
|
||||
// const image = this.valuesImage.find(item => item.identifier === fileName).value
|
||||
const image = this.resource[keyValue]
|
||||
if (this.isEmptyValue(image)) {
|
||||
return this.defaultImage
|
||||
}
|
||||
return image
|
||||
},
|
||||
setCurrent(row) {
|
||||
this.$refs.singleTable.setCurrentRow(row)
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.currentRow = val
|
||||
this.setCurrent(this.currentRow)
|
||||
},
|
||||
keyAction(event) {
|
||||
switch (event.srcKey) {
|
||||
case 'refreshList':
|
||||
/**
|
||||
* TODO: When refreshing you are making 2 list requests, you can be the
|
||||
* observer that activates the second request
|
||||
*/
|
||||
this.loadProductsPricesList()
|
||||
break
|
||||
|
||||
case 'closeProductList':
|
||||
this.$store.commit('showListProductPrice', {
|
||||
attribute: this.popoverName,
|
||||
isShowed: false
|
||||
})
|
||||
break
|
||||
}
|
||||
},
|
||||
loadProductsPricesList() {
|
||||
this.$store.dispatch('listProductPriceFromServerProductInfo', {})
|
||||
},
|
||||
/**
|
||||
* @param {number} newPage
|
||||
*/
|
||||
handleChangePage(newPage) {
|
||||
this.$store.dispatch('setProductPicePageNumber', newPage)
|
||||
},
|
||||
findlistProductWithRow(row) {
|
||||
if (!this.isSelectable) {
|
||||
return
|
||||
}
|
||||
// TODO: Change this dispatch for set values with local methods, to delete subscripton
|
||||
this.$store.dispatch('notifyActionKeyPerformed', {
|
||||
containerUuid: 'POS',
|
||||
columnName: 'ProductValue',
|
||||
// TODO: Verify with 'value' or 'searchValue' attribute
|
||||
value: row.product.name
|
||||
})
|
||||
|
||||
// close popover of list product price
|
||||
this.$store.commit('showListProductPrice', {
|
||||
attribute: this.popoverName,
|
||||
isShowed: false
|
||||
})
|
||||
},
|
||||
getTaxAmount(basePrice, taxRate) {
|
||||
if (this.isEmptyValue(basePrice) || this.isEmptyValue(taxRate)) {
|
||||
return 0
|
||||
}
|
||||
return (basePrice * taxRate) / 100
|
||||
},
|
||||
subscribeChanges() {
|
||||
return this.$store.subscribe((mutation, state) => {
|
||||
// if (!this.isEmptyValue(this.listWithPrice)) {
|
||||
// this.setCurrent(this.listWithPrice[0])
|
||||
// }
|
||||
if (mutation.type === 'updateValueOfField' &&
|
||||
!mutation.payload.columnName.includes('DisplayColumn') &&
|
||||
mutation.payload.containerUuid === this.metadata.containerUuid) {
|
||||
clearTimeout(this.timeOut)
|
||||
this.timeOut = setTimeout(() => {
|
||||
this.$store.dispatch('updateSearch', mutation.payload.value)
|
||||
this.$store.commit('setIsReloadProductPrice')
|
||||
}, 1000)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -182,6 +182,7 @@ export default {
|
||||
|
||||
this.timeOut = setTimeout(() => {
|
||||
this.$store.dispatch('listProductPriceFromServer', {
|
||||
containerUuid: 'Products-Price-List',
|
||||
pageNumber: 1,
|
||||
searchValue: stringToMatch
|
||||
})
|
||||
|
@ -34,6 +34,9 @@ export default {
|
||||
case 'BarcodeReader':
|
||||
form = import('@/components/ADempiere/Form/BarcodeReader')
|
||||
break
|
||||
case 'ProductInfo':
|
||||
form = import('@/components/ADempiere/Form/ProductInfo')
|
||||
break
|
||||
case 'VPOS':
|
||||
form = import('@/components/ADempiere/Form/VPOS')
|
||||
break
|
||||
|
@ -98,6 +98,22 @@ const staticRoutes = [
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/ProductInfo',
|
||||
component: Layout,
|
||||
hidden: false,
|
||||
children: [
|
||||
{
|
||||
path: '/ProductInfo',
|
||||
component: () => import('@/views/ADempiere/Form'),
|
||||
name: 'ProductInfo',
|
||||
meta: {
|
||||
title: 'ProductInfo',
|
||||
isIndex: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -49,7 +49,7 @@ export default {
|
||||
})
|
||||
})
|
||||
.catch(error => {
|
||||
console.warn(`Error getting epale error en guardar: ${error.message}. Code: ${error.code}.`)
|
||||
console.warn(`Error getting ProductInfo error en guardar: ${error.message}. Code: ${error.code}.`)
|
||||
})
|
||||
},
|
||||
listChatEntries({ commit }, {
|
||||
|
@ -18,7 +18,8 @@ const listProductPrice = {
|
||||
...withoutResponse,
|
||||
isShowPopoverField: false, // with field
|
||||
isShowPopoverMenu: false // with menu
|
||||
}
|
||||
},
|
||||
searchProduct: ''
|
||||
},
|
||||
mutations: {
|
||||
setListProductPrice(state, productsPrices) {
|
||||
@ -36,6 +37,9 @@ const listProductPrice = {
|
||||
setIsReloadProductPrice(state) {
|
||||
Vue.set(state.productPrice, 'isReload', true)
|
||||
Vue.set(state.productPrice, 'isLoaded', false)
|
||||
},
|
||||
updtaeSearchProduct(state, searchProduct) {
|
||||
state.searchProduct = searchProduct
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
@ -61,7 +65,6 @@ const listProductPrice = {
|
||||
console.warn(message)
|
||||
return
|
||||
}
|
||||
|
||||
commit('setIsReloadProductPrice')
|
||||
let pageToken, token
|
||||
if (isEmptyValue(pageNumber)) {
|
||||
@ -87,7 +90,6 @@ const listProductPrice = {
|
||||
columnName: 'ProductValue'
|
||||
})
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
requestListProductPrice({
|
||||
searchValue,
|
||||
@ -120,6 +122,82 @@ const listProductPrice = {
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
listProductPriceFromServerProductInfo({ state, commit, rootGetters }, {
|
||||
containerUuid = 'Products-Price-List-ProductInfo',
|
||||
pageNumber, // 1
|
||||
searchValue
|
||||
}) {
|
||||
const posUuid = rootGetters.getPointOfSalesUuid
|
||||
if (isEmptyValue(posUuid)) {
|
||||
const message = 'Sin punto de venta seleccionado'
|
||||
showMessage({
|
||||
type: 'info',
|
||||
message
|
||||
})
|
||||
console.warn(message)
|
||||
return
|
||||
}
|
||||
commit('setIsReloadProductPrice')
|
||||
let pageToken, token
|
||||
if (isEmptyValue(pageNumber)) {
|
||||
pageNumber = state.productPrice.pageNumber
|
||||
if (isEmptyValue(pageNumber)) {
|
||||
pageNumber = 1
|
||||
}
|
||||
|
||||
token = state.productPrice.token
|
||||
if (!isEmptyValue(token)) {
|
||||
pageToken = token + '-' + pageNumber
|
||||
}
|
||||
}
|
||||
|
||||
const { priceList, templateBusinessPartner } = rootGetters.getCurrentPOS
|
||||
const { uuid: businessPartnerUuid } = templateBusinessPartner
|
||||
const { uuid: priceListUuid } = priceList
|
||||
const { uuid: warehouseUuid } = rootGetters['user/getWarehouse']
|
||||
|
||||
if (isEmptyValue(searchValue)) {
|
||||
searchValue = rootGetters.getValueOfField({
|
||||
containerUuid,
|
||||
columnName: 'ProductValue'
|
||||
})
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
requestListProductPrice({
|
||||
searchValue,
|
||||
priceListUuid,
|
||||
businessPartnerUuid,
|
||||
warehouseUuid,
|
||||
pageToken
|
||||
}).then(responseProductPrice => {
|
||||
if (isEmptyValue(token) || isEmptyValue(pageToken)) {
|
||||
token = extractPagingToken(responseProductPrice.nextPageToken)
|
||||
}
|
||||
|
||||
commit('setListProductPrice', {
|
||||
...responseProductPrice,
|
||||
isLoaded: true,
|
||||
isReload: false,
|
||||
businessPartnerUuid,
|
||||
warehouseUuid,
|
||||
token,
|
||||
pageNumber
|
||||
})
|
||||
|
||||
resolve(responseProductPrice)
|
||||
}).catch(error => {
|
||||
console.warn(`getKeyLayoutFromServer: ${error.message}. Code: ${error.code}.`)
|
||||
showMessage({
|
||||
type: 'error',
|
||||
message: error.message,
|
||||
showClose: true
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
updateSearch({ commit }, newValue) {
|
||||
commit('updtaeSearchProduct', newValue)
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
@ -131,6 +209,9 @@ const listProductPrice = {
|
||||
}
|
||||
}
|
||||
return state.productPrice
|
||||
},
|
||||
getSearchProduct: (state) => {
|
||||
return state.searchProduct
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user