diff --git a/packageUser/self-report/create.vue b/packageUser/self-report/create.vue
index 69663bb..73b3041 100644
--- a/packageUser/self-report/create.vue
+++ b/packageUser/self-report/create.vue
@@ -74,46 +74,33 @@
销量填写
-
-
-
- 新增商品
+
+
+
+
+
+
+
+ {{ totalSalesItemCount > 0 ? '已填写销量商品' : '去填写销量' }}
+
+
+ {{ totalSalesItemCount > 0 ? `共 ${totalSalesItemCount} 项,点击可继续修改` : '支持搜索、分类筛选与批量填写' }}
+
+
+
+
+ {{ totalSalesItemCount }}
+
- 请点击“新增商品”逐个添加销量商品
-
-
-
-
- {{ item.name }}
- {{ item.groupTitle }}
-
- 删除
-
-
-
- 厂家
- {{ item.makerAbb }}
-
-
- 编码
- {{ item.goodsCode || '-' }}
-
-
-
-
-
-
- {{ item.spec || '规格' }}
-
-
-
-
-
-
- {{ item.unit || '盒' }}
-
-
+ 请点击上方进入「销量填写」添加商品
+
+
+ {{ item.name }}
+ {{ formatSalesQty(item.qty) }}{{ item.unit || '盒' }}
+
+
+ 等共 {{ totalSalesItemCount }} 项
@@ -192,7 +179,7 @@
保存草稿
- {{ submitLoading ? '提交中...' : (isResubmit ? '重新提交' : '提交销量') }}
+ {{ submitLoading ? '提交中...' : (isResubmit ? '重新提交' : '提交填报') }}
@@ -253,60 +240,6 @@
-
-
-
- 取消
- 选择商品
- 完成
-
-
-
-
-
-
-
-
-
-
- 暂无可选商品
- 该分类暂无可选商品
-
-
-
-
- {{ item.name }}
- {{ item.makerAbb || '-' }} / {{ item.spec || '规格' }}
-
-
- {{ isProductSelected(item) ? '已添加' : '添加' }}
-
-
-
-
-
-
-
{
- const items = Array.isArray(group.items) ? group.items : []
- const nextItems = items.filter(item => {
- const name = String(item.name || '').toLowerCase()
- const maker = String(item.makerAbb || '').toLowerCase()
- const spec = String(item.spec || '').toLowerCase()
- return name.includes(keyword) || maker.includes(keyword) || spec.includes(keyword)
- })
- return Object.assign({}, group, {items: nextItems})
- })
- .filter(group => Array.isArray(group.items) && group.items.length)
- },
- currentProductGroupKey() {
- const groups = Array.isArray(this.filteredProductGroups) ? this.filteredProductGroups : []
- if (!groups.length) return ''
- const hasActive = groups.some(group => group.key === this.activeProductGroupKey)
- return hasActive ? this.activeProductGroupKey : groups[0].key
- },
- displayedProductOptions() {
- const groups = Array.isArray(this.filteredProductGroups) ? this.filteredProductGroups : []
- const current = groups.find(group => group.key === this.currentProductGroupKey)
- return current && Array.isArray(current.items) ? current.items : []
- },
totalSalesItemCount() {
return Array.isArray(this.salesItems) ? this.salesItems.length : 0
},
+ // 填报页只做摘要展示,明细与编辑都在「销量填写」页
+ salesSummaryLimit() {
+ return 3
+ },
+ salesSummaryRows() {
+ const items = Array.isArray(this.salesItems) ? this.salesItems : []
+ return items.slice(0, this.salesSummaryLimit)
+ },
totalRewardAmount() {
return (Array.isArray(this.salesItems) ? this.salesItems : []).reduce((sum, item) => {
return sum + (this.parseSalesQty(item && item.qty) * this.getTaskIncomeNumber(item))
@@ -476,9 +388,43 @@ export default {
} else {
await this.checkDraftOnEntry()
}
+ return
}
+ // 从「销量填写」页返回时接收交接数据(仅在用户主动保存/提交后才有 action)
+ this.consumeSalesHandoff()
},
methods: {
+ // 销量页只回传数据,整单的提交动作仍由本页的「提交填报」统一完成。
+ // action='submit' 为销量页点了它自己的「提交销量」,'merge' 为返回时同步明细,
+ // 两者都只做合并,不代表本页已提交。
+ consumeSalesHandoff() {
+ const handoff = getSalesHandoff()
+ if (!handoff || !handoff.action) return
+ const items = Array.isArray(handoff.items) ? handoff.items : []
+ this.salesItems = cloneSalesItems(items)
+ clearSalesHandoff()
+ if (handoff.action === 'submit' && !items.length) {
+ uni.showToast({title: '销量商品不能为空', icon: 'none'})
+ }
+ },
+ openSalesEntry() {
+ if (!this.form.chainId || !this.form.merchantId) {
+ uni.showToast({title: '请先选择连锁和门店', icon: 'none'})
+ return
+ }
+ // 销量页需要连锁名去拉商品库,进页前先把当前明细交出去
+ setSalesHandoff({
+ action: '',
+ chainName: this.form.chainName || '',
+ items: cloneSalesItems(this.salesItems)
+ })
+ uni.navigateTo({
+ url: '/packageUser/self-report/sales',
+ fail: () => {
+ uni.showToast({title: '页面打开失败', icon: 'none'})
+ }
+ })
+ },
buildForm() {
return {
chainId: '',
@@ -503,19 +449,14 @@ export default {
]
this.boothPhotos = []
this.receiptPhotos = []
- this.productGroups = []
this.salesItems = []
- this.salesKeyword = ''
- this.showProductPicker = false
this.showChainPicker = false
- this.activeProductGroupKey = ''
this.currentAddress = ''
this.lat = null
this.lng = null
this.currentProvince = ''
this.currentCity = ''
await this.getMerchantList()
- await this.getProductGroups()
},
getDraftStorageKey() {
const user = getUserInfo() || {}
@@ -644,10 +585,6 @@ export default {
if (!this.currentAddress || !this.lat || !this.lng) {
await this.fetchCurrentLocation()
}
- // 销量商品按连锁重新拉取,保证规格选项与当前连锁一致
- if (this.form.chainName) {
- await this.getProductGroups()
- }
} catch (error) {
await this.toast(error && error.message ? error.message : '填报记录加载失败')
} finally {
@@ -809,13 +746,8 @@ export default {
this.lng = draft.lng
this.currentProvince = draft.currentProvince || ''
this.currentCity = draft.currentCity || ''
- this.salesKeyword = ''
- this.showProductPicker = false
this.showChainPicker = false
this.showMerchantPicker = false
- if (this.form.chainName) {
- await this.getProductGroups()
- }
},
async fetchCurrentLocation() {
const location = await getLocationOnly().catch(() => null)
@@ -1033,14 +965,12 @@ export default {
this.form.lng = ''
this.merchantStoreSearchKeyword = ''
this.merchantPickerValue = [0]
+ // 商品库按连锁过滤,换连锁后原销量明细已不适用。
+ // 草稿不清:按规则只有「提交成功」与「恢复询问时放弃」才丢弃草稿。
this.salesItems = []
- this.productGroups = []
- this.productLoadedChainName = ''
- this.activeProductGroupKey = ''
- this.salesKeyword = ''
+ clearSalesHandoff()
}
this.closeChainPicker()
- if (changed) await this.getProductGroups()
},
openMerchantPicker() {
if (!this.form.chainId) {
@@ -1091,285 +1021,14 @@ export default {
const prevChainName = String(this.form.chainName || '').trim()
this.applyMerchant(chain, selected)
const nextChainName = String(this.form.chainName || '').trim()
- this.salesKeyword = ''
- this.productGroups = []
- this.productLoadedChainName = ''
- this.activeProductGroupKey = ''
this.closeMerchantPicker()
if (nextChainName && nextChainName !== prevChainName) {
- await this.getProductGroups()
+ // 换连锁后商品库与销量明细不再适用。
+ // 草稿不清:按规则只有「提交成功」与「恢复询问时放弃」才丢弃草稿。
+ this.salesItems = []
+ clearSalesHandoff()
}
},
- isProductNode(node) {
- if (!node || typeof node !== 'object') return false
- const product = node.product && typeof node.product === 'object' ? node.product : {}
- const hasChildrenOnly = Array.isArray(node.children)
- && !this.pickFirstText(node, ['goodsName', 'goods_name', 'goodsCode', 'code', 'productCode', 'batchCode'])
- && !node.product
- if (hasChildrenOnly) return false
- return !!(
- this.pickFirstText(node, ['goodsName', 'goods_name', 'goodsCode', 'code', 'productCode', 'batchCode']) ||
- this.pickFirstText(product, ['goodsName', 'goods_name', 'goodsCode', 'code', 'productCode', 'batchCode'])
- )
- },
- collectProductNodes(node, depth = 0) {
- if (!node || depth > 4) return []
- if (Array.isArray(node)) {
- return node.reduce((acc, item) => acc.concat(this.collectProductNodes(item, depth + 1)), [])
- }
- if (typeof node !== 'object') return []
- if (this.isProductNode(node)) return [node]
- const keys = ['productList', 'products', 'list', 'records', 'rows', 'items', 'children', 'data']
- return keys.reduce((acc, key) => {
- const value = node[key]
- if (Array.isArray(value) || (value && typeof value === 'object')) {
- return acc.concat(this.collectProductNodes(value, depth + 1))
- }
- return acc
- }, [])
- },
- buildCategoryTabs(rawData) {
- const rootList = this.unwrapList(rawData)
- const tabs = []
- rootList.forEach((categoryNode, categoryIndex) => {
- if (!categoryNode || typeof categoryNode !== 'object') return
- const label = categoryNode.cate
- const products = Array.isArray(categoryNode.children) ? categoryNode.children : []
- if (!label || !products.length) return
- tabs.push({
- key: `cate_children_${categoryIndex}`,
- label,
- rawProducts: products
- })
- })
- if (tabs.length) return tabs
- const allProducts = this.collectProductNodes(rootList)
- if (allProducts.length) {
- tabs.push({
- key: 'all',
- label: '全部',
- rawProducts: allProducts
- })
- }
- return tabs
- },
- normalizeSpecOptions(item) {
- const raw = item.specOptions || item.specList || item.specs || []
- if (Array.isArray(raw)) {
- return raw.map(value => `${value}`.trim()).filter(Boolean)
- }
- if (typeof raw === 'string') {
- return raw.split(/[、,,]/).map(value => value.trim()).filter(Boolean)
- }
- const spec = item.spec || item.specification || item.standard || ''
- return spec ? [spec] : []
- },
- normalizeProduct(item) {
- const product = item && item.product ? item.product : {}
- const code = item.goodsCode || item.code || item.productCode || item.batchCode || product.code || product.goodsCode || ''
- const productId = item.productId || item.id || item.goodsId || item.skuId || product.productId || product.id || ''
- const name = item.goodsName || item.goods_name || item.title || item.name || product.title || product.goodsName || product.name || ''
- const maker = item.makerAbb || item.maker || item.manufacturer || product.makerAbb || product.maker || product.manufacturer || ''
- const spec = product.spec || item.spec || item.specification || ''
- const cover = item.cover || product.cover || ''
- const chainId = item.chainId || item.chain_id || product.chainId || product.chain_id || ''
- const chainName = item.chainName || item.chain_name || item.chain || product.chainName || product.chain_name || product.chain || ''
- const merchantId = item.merchantId || item.merchant_id || product.merchantId || product.merchant_id || ''
- const merchantName = item.merchantName || item.merchant_name || item.shopName || item.shop_name || product.merchantName || product.merchant_name || ''
- return {
- productId,
- goodsCode: code,
- name,
- cover,
- makerAbb: maker,
- spec,
- chainId,
- chainName,
- merchantId,
- merchantName,
- unit: item.unit || product.unit || '盒',
- taskIncome: this.resolveTaskIncome(item),
- specOptions: this.normalizeSpecOptions(Object.assign({}, product, item)),
- qty: ''
- }
- },
- hasDisplayValue(value) {
- return value !== undefined && value !== null && String(value).trim() !== ''
- },
- resolveTaskIncome(item) {
- if (!item || typeof item !== 'object') return ''
- const product = item.product && typeof item.product === 'object' ? item.product : {}
- const candidates = [
- item.taskIncome
- ]
- for (let i = 0; i < candidates.length; i += 1) {
- const value = candidates[i]
- if (this.hasDisplayValue(value)) return value
- }
- return ''
- },
- hasTaskIncome(item) {
- return this.hasDisplayValue(this.resolveTaskIncome(item))
- },
- formatTaskIncome(item) {
- const value = this.resolveTaskIncome(item)
- if (!this.hasDisplayValue(value)) return ''
- const num = Number(value)
- if (!Number.isFinite(num)) return String(value)
- return Number.isInteger(num) ? String(num) : num.toFixed(2)
- },
- parseSalesQty(value) {
- const num = Number(value)
- return Number.isFinite(num) ? num : 0
- },
- getTaskIncomeNumber(item) {
- const num = Number(this.resolveTaskIncome(item))
- return Number.isFinite(num) ? num : 0
- },
- formatMoney(value) {
- const num = Number(value)
- if (!Number.isFinite(num)) return '0.00'
- return num.toFixed(2)
- },
- filterProductGroupsByChain(groups = [], rawChainId = '') {
- const chainId = this.normalizeChainIdForApi(rawChainId)
- if (!chainId) return Array.isArray(groups) ? groups : []
- return (Array.isArray(groups) ? groups : [])
- .map(group => Object.assign({}, group, {
- items: (Array.isArray(group.items) ? group.items : []).filter(item => {
- const productChainId = String(item.chainId || '').trim()
- return !productChainId || productChainId === chainId
- })
- }))
- .filter(group => Array.isArray(group.items) && group.items.length)
- },
- buildSalesGroupsFromProductData(rawData) {
- const tabs = this.buildCategoryTabs(rawData)
- let rowSeed = 0
- const groups = tabs.map((tab, tabIndex) => {
- const items = (Array.isArray(tab.rawProducts) ? tab.rawProducts : [])
- .map((rawItem, itemIndex) => {
- const product = this.normalizeProduct(rawItem)
- if (!product.name) return null
- const specOptions = Array.isArray(product.specOptions) && product.specOptions.length
- ? product.specOptions
- : [product.spec || '规格']
- return {
- ...product,
- groupTitle: tab.label || `分类${tabIndex + 1}`,
- spec: product.spec || specOptions[0] || '规格',
- specOptions,
- _key: `sales_${tabIndex}_${itemIndex}_${rowSeed++}`
- }
- })
- .filter(Boolean)
- return {
- key: tab.key || `group_${tabIndex}`,
- title: tab.label || `分类${tabIndex + 1}`,
- items
- }
- }).filter(group => group.items.length)
- return groups
- },
- async getProductGroups() {
- const chainName = String(this.form.chainName || '').trim()
- if (!chainName) {
- this.productGroups = []
- this.productLoadedChainName = ''
- this.activeProductGroupKey = ''
- return
- }
- this.productLoading = true
- try {
- const {data} = await productGroupByCateReq({chain: chainName, type: 0})
- this.productGroups = this.buildSalesGroupsFromProductData(data)
- this.productLoadedChainName = chainName
- } catch (error) {
- this.productGroups = []
- this.productLoadedChainName = ''
- uni.showToast({
- title: '商品数据获取失败',
- icon: 'none'
- })
- } finally {
- this.activeProductGroupKey = this.productGroups[0] ? this.productGroups[0].key : ''
- this.productLoading = false
- }
- },
- async openProductPicker() {
- if (!this.form.chainName || !this.form.merchantId) {
- uni.showToast({
- title: '请先选择连锁和门店',
- icon: 'none'
- })
- return
- }
- const currentChainName = String(this.form.chainName || '').trim()
- if (!currentChainName) {
- uni.showToast({
- title: '当前连锁无效',
- icon: 'none'
- })
- return
- }
- if (this.productLoadedChainName !== currentChainName || !this.productGroups.length) {
- await this.getProductGroups()
- }
- if (!this.filteredProductGroups.length) {
- uni.showToast({
- title: '当前连锁暂无可选商品',
- icon: 'none'
- })
- return
- }
- if (!this.activeProductGroupKey) {
- this.activeProductGroupKey = this.filteredProductGroups[0] ? this.filteredProductGroups[0].key : ''
- }
- this.showProductPicker = true
- },
- closeProductPicker() {
- this.showProductPicker = false
- },
- selectProductGroup(key) {
- this.activeProductGroupKey = key
- },
- isProductSelected(product) {
- return (Array.isArray(this.salesItems) ? this.salesItems : []).some(item => {
- if (product.productId && item.productId) {
- return String(item.productId) === String(product.productId)
- }
- return item.name === product.name
- })
- },
- selectProduct(product) {
- if (this.isProductSelected(product)) {
- uni.showToast({
- title: '该商品已添加',
- icon: 'none'
- })
- return
- }
- const specOptions = Array.isArray(product.specOptions) && product.specOptions.length
- ? [...product.specOptions]
- : [product.spec || '规格']
- this.salesItems.push({
- ...product,
- groupTitle: product.groupTitle || '',
- specOptions,
- spec: product.spec || specOptions[0] || '规格',
- qty: ''
- })
- this.showProductPicker = false
- this.salesKeyword = ''
- },
- removeSalesItem(index) {
- this.salesItems.splice(index, 1)
- },
- onSpecChange(event, item) {
- const value = Number(event.detail.value || 0)
- const nextSpec = item.specOptions[value] || item.spec
- item.spec = nextSpec
- },
async ensureLogin() {
if (this.isLoggedIn) return true
this.$refs.Login.open()
@@ -1476,6 +1135,32 @@ export default {
if (emptyQty) item.qty = 0
})
},
+ hasDisplayValue(value) {
+ return value !== undefined && value !== null && String(value).trim() !== ''
+ },
+ resolveTaskIncome(item) {
+ if (!item || typeof item !== 'object') return ''
+ return this.hasDisplayValue(item.taskIncome) ? item.taskIncome : ''
+ },
+ parseSalesQty(value) {
+ const num = Number(value)
+ return Number.isFinite(num) ? num : 0
+ },
+ getTaskIncomeNumber(item) {
+ const num = Number(this.resolveTaskIncome(item))
+ return Number.isFinite(num) ? num : 0
+ },
+ formatMoney(value) {
+ const num = Number(value)
+ if (!Number.isFinite(num)) return '0.00'
+ return num.toFixed(2)
+ },
+ formatSalesQty(value) {
+ const empty = value === '' || value === null || value === undefined
+ if (empty) return '0'
+ const num = Number(value)
+ return Number.isFinite(num) ? String(num) : '0'
+ },
buildSubmitSalesGroups() {
const groupsMap = {}
;(Array.isArray(this.salesItems) ? this.salesItems : []).forEach(item => {
@@ -1596,10 +1281,13 @@ export default {
delete payload.status
payload.id = this.resubmitId
await selfReportResubmitReq(payload)
+ clearSalesHandoff()
await this.toast('重新提交成功,请等待审核')
} else {
await selfReportAddReq(this.buildPayload())
this.clearDraft()
+ // 整页已提交,交接数据不该留到下次
+ clearSalesHandoff()
await this.toast('提交成功,请等待审核')
}
uni.navigateBack({
@@ -1882,153 +1570,114 @@ export default {
justify-content: center;
}
-.sales-search {
- margin-bottom: 18rpx;
-}
-
-.sales-actions {
- margin-bottom: 18rpx;
-}
-
-.sales-add-btn {
- width: 240rpx;
- min-height: 76rpx;
- border-radius: 999rpx;
- background: linear-gradient(135deg, #ff8a4c, #ff6b4a);
- box-shadow: 0 12rpx 24rpx rgba(255, 107, 74, 0.2);
- display: inline-flex;
+.sales-entry {
+ display: flex;
align-items: center;
- justify-content: center;
- gap: 10rpx;
- color: #ffffff;
- font-size: 26rpx;
- font-weight: 700;
- letter-spacing: 1rpx;
-}
-
-.selected-sales-list {
- display: flex;
- flex-direction: column;
- gap: 18rpx;
-}
-
-.selected-sales-item {
- padding: 22rpx;
- border-radius: 22rpx;
- background: linear-gradient(180deg, #fffaf5, #ffffff);
- border: 1rpx solid #fde7d8;
-}
-
-.selected-sales-head {
- display: flex;
- align-items: flex-start;
justify-content: space-between;
gap: 20rpx;
+ padding: 26rpx 24rpx;
+ border-radius: 20rpx;
+ background: linear-gradient(135deg, #fff6ef, #fffaf6);
+ border: 2rpx solid #ffd9c2;
}
-.selected-sales-name {
- font-size: 30rpx;
- font-weight: 700;
- color: #0f172a;
-}
-
-.selected-sales-group {
- margin-top: 8rpx;
- font-size: 22rpx;
- color: #c2410c;
-}
-
-.selected-sales-remove {
- color: #ef4444;
- font-size: 24rpx;
-}
-
-.selected-sales-meta {
- margin-top: 16rpx;
+.sales-entry-left {
+ flex: 1;
+ min-width: 0;
display: flex;
- flex-wrap: wrap;
- gap: 14rpx;
-}
-
-.selected-sales-meta-item {
- min-width: 220rpx;
- padding: 12rpx 16rpx;
- border-radius: 16rpx;
- background: #fff7ed;
-}
-
-.meta-label {
- color: #9a3412;
- font-size: 22rpx;
-}
-
-.meta-value {
- display: block;
- margin-top: 4rpx;
- color: #7c2d12;
- font-size: 24rpx;
-}
-
-.sales-controls {
- margin-top: 16rpx;
- display: flex;
- align-items: flex-start;
- justify-content: space-between;
+ align-items: center;
gap: 18rpx;
}
-.spec-wrap {
+.sales-entry-icon {
+ flex-shrink: 0;
+ width: 76rpx;
+ height: 76rpx;
+ border-radius: 20rpx;
+ background: #ffffff;
+ border: 1rpx solid #ffd9c2;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.sales-entry-text {
flex: 1;
min-width: 0;
}
-.spec-picker {
- display: block;
-}
-
-.spec-pill {
- min-height: 72rpx;
- border-radius: 999rpx;
- background: #fff7ed;
- border: 1rpx solid #fdba74;
- color: #c2410c;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 0 22rpx;
- font-size: 24rpx;
-}
-
-.sales-income {
- margin-top: 10rpx;
- padding-left: 8rpx;
- color: #dc2626;
- font-size: 24rpx;
- line-height: 1.4;
-}
-
-.qty-box {
- width: 240rpx;
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 12rpx;
- padding: 0 20rpx;
- min-height: 72rpx;
- border-radius: 18rpx;
- background: #f8fafc;
- border: 1rpx solid #dbe3ef;
-}
-
-.qty-input {
- flex: 1;
- font-size: 30rpx;
+.sales-entry-title {
+ font-size: 29rpx;
+ font-weight: 700;
color: #0f172a;
}
-.qty-unit {
- color: #7a8495;
- font-size: 24rpx;
+.sales-entry-sub {
+ margin-top: 6rpx;
+ font-size: 23rpx;
+ color: #9aa3b2;
+}
+
+.sales-entry-right {
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ gap: 10rpx;
+}
+
+.sales-entry-count {
+ min-width: 40rpx;
+ height: 40rpx;
+ padding: 0 12rpx;
+ border-radius: 999rpx;
+ background: linear-gradient(135deg, #ff8a4c, #ff6b4a);
+ color: #ffffff;
+ font-size: 22rpx;
+ font-weight: 700;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.sales-summary-list {
+ margin-top: 18rpx;
+ display: flex;
+ flex-direction: column;
+ gap: 12rpx;
+}
+
+.sales-summary-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16rpx;
+ padding: 16rpx 20rpx;
+ border-radius: 14rpx;
+ background: #f8fafc;
+}
+
+.sales-summary-name {
+ flex: 1;
+ min-width: 0;
+ font-size: 26rpx;
+ color: #374151;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+.sales-summary-qty {
+ flex-shrink: 0;
+ font-size: 26rpx;
+ font-weight: 700;
+ color: #ff7a35;
+}
+
+.sales-summary-more {
+ padding-top: 4rpx;
+ text-align: center;
+ font-size: 23rpx;
+ color: #9aa3b2;
}
.summary-input {
@@ -2158,104 +1807,6 @@ export default {
color: #1f2937;
}
-.product-picker {
- background: #ffffff;
- padding-bottom: calc(env(safe-area-inset-bottom) + 12rpx);
-}
-
-.product-panel {
- display: flex;
- height: 62vh;
- border-top: 1rpx solid #eef2f7;
-}
-
-.category-sidebar {
- width: 220rpx;
- background: linear-gradient(180deg, #f8fafc, #fff7ed);
- border-right: 1rpx solid #eef2f7;
-}
-
-.category-item {
- min-height: 88rpx;
- padding: 18rpx 16rpx;
- box-sizing: border-box;
- display: flex;
- align-items: center;
- justify-content: center;
- text-align: center;
- color: #64748b;
- font-size: 24rpx;
- border-bottom: 1rpx solid rgba(226, 232, 240, 0.8);
-}
-
-.category-item-active {
- background: #ffffff;
- color: #ea580c;
- font-weight: 700;
-}
-
-.product-panel-main {
- flex: 1;
- padding: 20rpx 24rpx 24rpx;
- box-sizing: border-box;
-}
-
-.product-picker-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 18rpx;
- padding: 18rpx 20rpx;
- border-radius: 18rpx;
- background: #f8fafc;
- border: 1rpx solid #e2e8f0;
-}
-
-.product-picker-item + .product-picker-item {
- margin-top: 12rpx;
-}
-
-.product-picker-main {
- flex: 1;
- min-width: 0;
-}
-
-.product-picker-cover {
- flex-shrink: 0;
- width: 96rpx;
- height: 96rpx;
- border-radius: 16rpx;
- background: #ffffff;
-}
-
-.product-picker-name {
- color: #0f172a;
- font-size: 28rpx;
- font-weight: 600;
-}
-
-.product-picker-sub {
- margin-top: 6rpx;
- color: #64748b;
- font-size: 22rpx;
-}
-
-.product-picker-income {
- margin-top: 6rpx;
- color: #dc2626;
- font-size: 22rpx;
-}
-
-.product-picker-tag {
- flex-shrink: 0;
- padding: 8rpx 18rpx;
- border-radius: 999rpx;
- background: #dcfce7;
- color: #15803d;
- font-size: 22rpx;
- font-weight: 700;
-}
-
.audit-empty-tip {
padding: 30rpx 0;
text-align: center;
diff --git a/packageUser/self-report/sales.vue b/packageUser/self-report/sales.vue
new file mode 100644
index 0000000..bd0a574
--- /dev/null
+++ b/packageUser/self-report/sales.vue
@@ -0,0 +1,1258 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 筛选
+
+
+
+
+
+
+
+ 新增商品
+
+
+
+ 批量填写
+
+
+
+
+
+ 还没有添加商品
+ 点击上方「新增商品」从商品库中选择
+
+
+
+
+ 没有匹配的商品
+ 换个关键词或清空筛选条件试试
+
+
+
+
+ {{ formatBadge(row.index) }}
+
+
+
+
+
+
+
+ {{ row.item.name }}
+
+ 删除
+
+
+
+
+ {{ row.item.groupTitle || '自定义商品' }}
+
+
+
+ 厂家
+ {{ row.item.makerAbb || '-' }}
+
+
+ 编码
+ {{ row.item.goodsCode || '-' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 按分类筛选
+ 完成
+
+
+
+ 全部分类
+
+
+
+ {{ option }}
+
+
+
+
+
+
+
+
+
+ 取消
+ 批量填写
+ 保存
+
+
+ 还没有添加商品
+ 请先新增商品再来批量填写
+
+
+
+
+ {{ row.name }}
+ {{ row.groupTitle || '自定义商品' }} · {{ row.spec || '规格' }}
+
+
+ {{ row.unit || '盒' }}
+
+
+
+
+
+
+
+
+ 取消
+ 选择商品
+ 完成
+
+
+
+
+
+
+
+
+
+
+ 暂无可选商品
+ 该分类暂无可选商品
+
+
+
+
+ {{ item.name }}
+ {{ item.makerAbb || '-' }} / {{ item.spec || '规格' }}
+
+
+ {{ isProductSelected(item) ? '已添加' : '添加' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages.json b/pages.json
index dd0c9c3..765af2c 100644
--- a/pages.json
+++ b/pages.json
@@ -170,6 +170,13 @@
"navigationStyle": "custom"
}
},
+ {
+ "path": "self-report/sales",
+ "style": {
+ "navigationBarTitleText": "销量填写",
+ "navigationStyle": "custom"
+ }
+ },
{
"path": "self-report/records",
"style": {
diff --git a/util/sales-handoff.js b/util/sales-handoff.js
new file mode 100644
index 0000000..81d2e68
--- /dev/null
+++ b/util/sales-handoff.js
@@ -0,0 +1,44 @@
+import {getUserInfo} from "@/util/user";
+
+// 「自主填报」(create.vue) 与「销量填写」(sales.vue) 两个页面之间的数据交接。
+// 本工程没有全局状态管理,页面间只能靠 storage 传递。
+//
+// 这里只负责一次性的交接,不是草稿:
+// - 进销量页前,create 页写入连锁名与当前明细作为上下文;
+// - 销量页返回时写回明细(action='submit' 表示用户点了销量页自己的
+// 「提交销量」,'merge' 表示按返回键离开时同步明细);
+// - create 页消费后即清。两者都不代表整单已提交,整单提交是
+// create 页「提交填报」按钮的职责。
+//
+// 持久化草稿统一由 create 页的整单草稿负责(含 salesItems/打卡/小票/小结),
+// 销量页不再单独存草稿,避免两份状态互相覆盖。
+const normalizeUid = () => {
+ const user = getUserInfo() || {}
+ const uid = user.uid || user.id || user.userId || 'guest'
+ return String(uid)
+}
+
+const readObject = key => {
+ const value = uni.getStorageSync(key)
+ return value && typeof value === 'object' ? value : null
+}
+
+export const cloneSalesItems = (items = []) => {
+ try {
+ return JSON.parse(JSON.stringify(Array.isArray(items) ? items : []))
+ } catch (error) {
+ return []
+ }
+}
+
+export const getSalesHandoffKey = () => `yxe_self_report_sales_handoff_${normalizeUid()}`
+
+export const setSalesHandoff = (payload = {}) => {
+ uni.setStorageSync(getSalesHandoffKey(), Object.assign({savedAt: Date.now()}, payload))
+}
+
+export const getSalesHandoff = () => readObject(getSalesHandoffKey())
+
+export const clearSalesHandoff = () => {
+ uni.removeStorageSync(getSalesHandoffKey())
+}