一键发布
Some checks failed
Some checks failed
This commit is contained in:
parent
02373f243e
commit
40660f5beb
@ -58,7 +58,7 @@ export const kongfzLogin = (username, password, ip, port) => {
|
||||
|
||||
/**
|
||||
* 批量提交 Token 到核价器
|
||||
* @param {Array<{username: string, token: string}>} tokens - 账号 Token 列表
|
||||
* @param {Array<{username: string, token: string, login_name: string}>} tokens - 账号 Token 列表
|
||||
* @param {string} ip - 核价器 IP
|
||||
* @param {string} port - 核价器端口
|
||||
* @returns {Promise}
|
||||
|
||||
@ -63,13 +63,16 @@ export const fetchInventoryStats = async (warehouse_id) => {
|
||||
* 按库位分组获取商品库存列表 GET /api/inventory/grouped-list
|
||||
* 返回按库位分组的库存数据,每组包含库位信息和该库位下的商品列表
|
||||
* @param {Object} params - 请求参数
|
||||
* @param {string} [params.keyword] - 搜索关键词(库位编号/商品名称/条码)
|
||||
* @param {number} [params.warehouse_id] - 仓库ID
|
||||
* @param {number} [params.location_id] - 库位ID
|
||||
* @param {number} [params.page] - 当前页码
|
||||
* @param {number} [params.pageSize] - 每页条数
|
||||
* @returns {Promise<{ list: Array, total: number }>}
|
||||
*/
|
||||
export const fetchGoodsListByLocation = async ({ keyword, page, pageSize } = {}) => {
|
||||
export const fetchGoodsListByLocation = async ({ warehouse_id, location_id, keyword, page, pageSize } = {}) => {
|
||||
const params = {
|
||||
warehouse_id: warehouse_id || undefined,
|
||||
location_id: location_id || undefined,
|
||||
keyword: keyword || undefined,
|
||||
page,
|
||||
page_size: pageSize
|
||||
|
||||
@ -43,11 +43,12 @@ const normalizeListResponse = (payload) => {
|
||||
* @param {string} [params.page_size] - 每页条数
|
||||
* @returns {Promise<{ list: Array, total: number }>} 标准化后的出库单列表
|
||||
*/
|
||||
export const fetchOutboundList = async ({ out_no, status, warehouse_id, customer_id, shop_type, sales_order_id, start_date, end_date, association_order_no, logistics_no, page, page_size }) => {
|
||||
export const fetchOutboundList = async ({ out_no, status, warehouse_id, location_id, customer_id, shop_type, sales_order_id, start_date, end_date, association_order_no, logistics_no, page, page_size }) => {
|
||||
const params = {
|
||||
out_no: out_no || undefined,
|
||||
status: status || undefined,
|
||||
warehouse_id: warehouse_id || undefined,
|
||||
location_id: location_id || undefined,
|
||||
customer_id: customer_id || undefined,
|
||||
shop_type: shop_type || undefined,
|
||||
sales_order_id: sales_order_id || undefined,
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
import request from '@/utils/request'
|
||||
import axios from 'axios'
|
||||
|
||||
/** 商品模块 API 基础路径 */
|
||||
const API_BASE = '/product'
|
||||
|
||||
/** 不知鱼书 API 基础地址 */
|
||||
// const BZY_API_BASE = 'http://192.168.101.127:8080'
|
||||
const BZY_API_BASE = 'https://api.buzhiyushu.cn'
|
||||
|
||||
/**
|
||||
* 标准化列表接口返回的数据格式
|
||||
* @param {Object} payload - 接口返回的原始响应对象,通常包含 data 字段
|
||||
@ -397,4 +402,27 @@ export const restoreProduct = async ({ destroy_log_id }) => {
|
||||
*/
|
||||
export const syncGoodsFromWdt = async ({ start_time, end_time }) => {
|
||||
return request.get('/wangdian/query-goods', { params: { start_time, end_time } })
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布商品到选中店铺
|
||||
* @param {Object} params
|
||||
* @param {number} params.oneClick - 是否一键发布 1=是 0=否
|
||||
* @param {number} params.userId - 用户的 about_id
|
||||
* @param {string} [params.shopIds] - 选中的店铺ID,逗号分隔(一键发布时使用)
|
||||
* @param {string} [params.productIds] - 选中的商品ID,逗号分隔(普通发布时使用)
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const releaseGoods = async ({ oneClick, userId, shopIds, productIds }) => {
|
||||
const res = await axios.post(`${BZY_API_BASE}/zhishu/product/releaseGoods`, {
|
||||
oneClick,
|
||||
userId,
|
||||
...(shopIds !== undefined && { shopIds }),
|
||||
...(productIds !== undefined && { productIds })
|
||||
})
|
||||
const data = res.data
|
||||
if (data && data.code !== 200) {
|
||||
throw { response: { data } }
|
||||
}
|
||||
return data
|
||||
}
|
||||
@ -25,6 +25,7 @@ const normalizeListResponse = (payload) => {
|
||||
* @param {number} [params.status] - 订单状态筛选
|
||||
* @param {number} [params.customer_id] - 平台ID筛选
|
||||
* @param {number} [params.warehouse_id] - 仓库ID筛选
|
||||
* @param {number} [params.location_id] - 库位ID筛选
|
||||
* @param {number} [params.page] - 当前页码
|
||||
* @param {number} [params.pageSize] - 每页条数
|
||||
* @param {string} [params.sort_by] - 排序字段
|
||||
@ -33,12 +34,13 @@ const normalizeListResponse = (payload) => {
|
||||
* @param {string} [params.logistics_no] - 快递单号
|
||||
* @returns {Promise<{ list: Array, total: number }>} 标准化后的销售订单列表
|
||||
*/
|
||||
export const fetchSalesOrderList = async ({ keyword, status, shop_type, warehouse_id, page, pageSize, sort_by, sort_order, association_order_no, logistics_no }) => {
|
||||
export const fetchSalesOrderList = async ({ keyword, status, shop_type, warehouse_id, location_id, page, pageSize, sort_by, sort_order, association_order_no, logistics_no }) => {
|
||||
const params = {
|
||||
so_no: keyword || undefined,
|
||||
status,
|
||||
shop_type: shop_type || undefined,
|
||||
warehouse_id,
|
||||
location_id: location_id || undefined,
|
||||
page,
|
||||
page_size: pageSize,
|
||||
sort_by: sort_by || 'updated_at',
|
||||
@ -139,14 +141,22 @@ export const returnSalesOrderItem = async (data) => {
|
||||
* @param {Object} params - 请求参数
|
||||
* @param {number} params.page - 当前页码
|
||||
* @param {number} params.pageSize - 每页条数
|
||||
* @param {number} [params.warehouse_id] - 仓库ID
|
||||
* @param {number} [params.location_id] - 库位ID
|
||||
* @param {number} [params.status] - 状态
|
||||
* @param {string} [params.keyword] - 销售订单号
|
||||
* @param {string} [params.association_order_no] - 第三方订单编号
|
||||
* @param {string} [params.logistics_no] - 快递单号
|
||||
* @returns {Promise<{list: Array, total: number}>}
|
||||
*/
|
||||
export const fetchSalesOrderDetails = async ({ page, pageSize, shop_type, association_order_no, logistics_no }) => {
|
||||
export const fetchSalesOrderDetails = async ({ page, pageSize, warehouse_id, location_id, status, keyword, shop_type, association_order_no, logistics_no }) => {
|
||||
const params = {
|
||||
page,
|
||||
page_size: pageSize,
|
||||
warehouse_id: warehouse_id || undefined,
|
||||
location_id: location_id || undefined,
|
||||
status: status || undefined,
|
||||
so_no: keyword || undefined,
|
||||
shop_type: shop_type || undefined,
|
||||
association_order_no: association_order_no || undefined,
|
||||
logistics_no: logistics_no || undefined
|
||||
|
||||
@ -38,12 +38,13 @@ const normalizeListResponse = (payload) => {
|
||||
* @param {number} [params.pageSize] - 每页条数
|
||||
* @returns {Promise<{ list: Array, total: number }>} 标准化后的发货单列表
|
||||
*/
|
||||
export const fetchShippingOrderList = async ({ check_no, status, shop_type, warehouse_id, sales_order_id, wave_task_id, association_order_no, logistics_no, page, pageSize }) => {
|
||||
export const fetchShippingOrderList = async ({ check_no, status, shop_type, warehouse_id, location_id, sales_order_id, wave_task_id, association_order_no, logistics_no, page, pageSize }) => {
|
||||
const params = {
|
||||
check_no: check_no || undefined,
|
||||
status,
|
||||
shop_type: shop_type || undefined,
|
||||
warehouse_id,
|
||||
location_id: location_id || undefined,
|
||||
sales_order_id,
|
||||
wave_task_id,
|
||||
association_order_no: association_order_no || undefined,
|
||||
|
||||
@ -62,9 +62,7 @@
|
||||
<Search />
|
||||
</el-icon></template>
|
||||
</el-input>
|
||||
<el-select v-model="summaryParams.warehouse_id" placeholder="仓库" clearable style="width: 140px">
|
||||
<el-option v-for="item in warehouseOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
<WarehouseSelect v-model="summaryParams.location_id" @warehouse-change="onSummaryWarehouseChange" />
|
||||
<el-button type="primary" :icon="Search" @click="loadSummaryList">搜索</el-button>
|
||||
<el-button :icon="Refresh" @click="resetSummaryParams">重置</el-button>
|
||||
</div>
|
||||
@ -74,8 +72,7 @@
|
||||
<el-table-column prop="barcode" label="ISBN/条码" min-width="130" show-overflow-tooltip align="center" />
|
||||
|
||||
<el-table-column label="货位" min-width="100" align="center">
|
||||
<template #default="{ row }">{{ row.warehouse_code || warehouseMap[row.warehouse_code] || '-' }}##{{
|
||||
row.location_code || warehouseMap[row.location_code] || '-' }}</template>
|
||||
<template #default="{ row }">{{ row.location_code || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="仓库" min-width="100" align="center">
|
||||
<template #default="{ row }">{{ row.warehouse_name || warehouseMap[row.warehouse_id] || '-' }}</template>
|
||||
@ -173,6 +170,7 @@ import { Search, Refresh, Box, Goods, CircleCheckFilled, CircleCheck, Location }
|
||||
import dayjs from 'dayjs'
|
||||
import { fetchInventoryList, fetchInventoryDetailList, inventorySummary } from '@/api/inventory'
|
||||
import { fetchWarehouseList } from '@/api/warehouse'
|
||||
import WarehouseSelect from '@/components/warehouseSelect/index.vue'
|
||||
|
||||
defineOptions({ name: 'InventoryByGoods' })
|
||||
|
||||
@ -207,7 +205,8 @@ const summaryList = ref<any[]>([])
|
||||
const summaryParams = reactive({
|
||||
isbn: '',
|
||||
name: '',
|
||||
warehouse_id: null as number | null
|
||||
warehouse_id: null as number | null,
|
||||
location_id: null as number | null
|
||||
})
|
||||
const summaryPagination = reactive({ current: 1, pageSize: 20, total: 0 })
|
||||
|
||||
@ -248,6 +247,7 @@ const loadSummaryList = async (): Promise<void> => {
|
||||
isbn: summaryParams.isbn || undefined,
|
||||
name: summaryParams.name || undefined,
|
||||
warehouse_id: summaryParams.warehouse_id || undefined,
|
||||
location_id: summaryParams.location_id || undefined,
|
||||
page: summaryPagination.current,
|
||||
page_size: summaryPagination.pageSize
|
||||
})
|
||||
@ -309,10 +309,16 @@ const navigateToShippingOrder = (shippingNo: string) => {
|
||||
}
|
||||
|
||||
// ==================== 重置 ====================
|
||||
const onSummaryWarehouseChange = (warehouseId: number | null) => {
|
||||
summaryParams.warehouse_id = warehouseId
|
||||
summaryParams.location_id = null
|
||||
}
|
||||
|
||||
const resetSummaryParams = (): void => {
|
||||
summaryParams.isbn = ''
|
||||
summaryParams.name = ''
|
||||
summaryParams.warehouse_id = null
|
||||
summaryParams.location_id = null
|
||||
summaryPagination.current = 1
|
||||
loadSummaryList()
|
||||
}
|
||||
|
||||
@ -39,6 +39,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-bar">
|
||||
<WarehouseSelect v-model="queryParams.location_id" @warehouse-change="onLocationWarehouseChange" />
|
||||
<el-button type="primary" :icon="Search" @click="loadList">搜索</el-button>
|
||||
<el-button :icon="Refresh" @click="resetQueryParams">重置</el-button>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
:data="summaryList"
|
||||
v-loading="summaryLoading"
|
||||
@ -165,10 +171,17 @@ import { Box, Goods, CircleCheckFilled, CircleCheck, Location, Search, Refresh }
|
||||
import dayjs from 'dayjs'
|
||||
import { fetchGoodsListByLocation, fetchInventoryDetailList, inventorySummary } from '@/api/inventory'
|
||||
import { fetchWarehouseList } from '@/api/warehouse'
|
||||
import WarehouseSelect from '@/components/warehouseSelect/index.vue'
|
||||
|
||||
const summaryLoading = ref(false)
|
||||
const summaryList = ref<any[]>([])
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
warehouse_id: null as number | null,
|
||||
location_id: null as number | null
|
||||
})
|
||||
|
||||
// 下拉选项(用于明细弹窗中的仓库名称显示)
|
||||
const warehouseOptions = ref<any[]>([])
|
||||
const warehouseMap = ref<Record<string, string>>({})
|
||||
@ -267,10 +280,24 @@ const loadOptions = async (): Promise<void> => {
|
||||
}
|
||||
}
|
||||
|
||||
const onLocationWarehouseChange = (warehouseId: number | null) => {
|
||||
queryParams.warehouse_id = warehouseId
|
||||
queryParams.location_id = null
|
||||
}
|
||||
|
||||
const resetQueryParams = (): void => {
|
||||
queryParams.warehouse_id = null
|
||||
queryParams.location_id = null
|
||||
loadList()
|
||||
}
|
||||
|
||||
const loadList = async (): Promise<void> => {
|
||||
summaryLoading.value = true
|
||||
try {
|
||||
const res = await fetchGoodsListByLocation()
|
||||
const res = await fetchGoodsListByLocation({
|
||||
warehouse_id: queryParams.warehouse_id || undefined,
|
||||
location_id: queryParams.location_id || undefined
|
||||
})
|
||||
summaryList.value = res.list || []
|
||||
} catch (error) {
|
||||
ElMessage.error({ message: '加载库位库存失败', customClass: 'scan-error-message' })
|
||||
|
||||
@ -48,13 +48,13 @@
|
||||
<el-button type="primary">导出</el-button>
|
||||
<el-button type="primary">导出模板</el-button>
|
||||
<el-button type="primary" @click="handleImportClick">导入</el-button>
|
||||
<el-button type="primary">发布</el-button>
|
||||
<el-button type="primary">一键发布</el-button>
|
||||
<el-button type="primary" @click="handlePublish">发布</el-button>
|
||||
<el-button type="primary" @click="handleOneClickPublish">一键发布</el-button>
|
||||
<el-button type="primary">库存同步</el-button>
|
||||
<el-button type="primary">批量修改货区</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="tableData" v-loading="loading" border stripe style="width: 100%"
|
||||
<el-table ref="productTableRef" :data="tableData" v-loading="loading" border stripe style="width: 100%"
|
||||
@selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column prop="name" label="商品名称" min-width="100" show-overflow-tooltip align="center" />
|
||||
@ -244,6 +244,49 @@
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 发布弹窗 -->
|
||||
<el-dialog v-model="publishDialogVisible" :title="publishMode === 'oneClick' ? '一键发布 - 选择店铺' : '发布 - 选择店铺'" width="750px" destroy-on-close
|
||||
@open="loadPublishShops">
|
||||
<div v-loading="publishLoading" class="publish-dialog-body">
|
||||
<template v-if="publishShopGroups.length > 0">
|
||||
<div v-for="group in publishShopGroups" :key="group.shop_type" class="publish-shop-group">
|
||||
<div class="publish-group-header">
|
||||
<el-checkbox
|
||||
:model-value="group.checkedIds.length === group.shops.length && group.shops.length > 0"
|
||||
:indeterminate="group.checkedIds.length > 0 && group.checkedIds.length < group.shops.length"
|
||||
@change="(val: boolean) => toggleGroupAll(group, val)"
|
||||
/>
|
||||
<span class="publish-group-title">{{ group.label }}</span>
|
||||
<span class="publish-group-count">({{ group.shops.length }} 个店铺)</span>
|
||||
</div>
|
||||
<el-checkbox-group v-model="group.checkedIds" class="publish-shop-cards">
|
||||
<el-checkbox
|
||||
v-for="shop in group.shops"
|
||||
:key="shop.shop_id"
|
||||
:label="shop.shop_id"
|
||||
class="publish-shop-card"
|
||||
border
|
||||
>
|
||||
<div class="shop-card-content">
|
||||
<div class="shop-card-name">{{ shop.shop_alias_name }}</div>
|
||||
<div class="shop-card-type">{{ shop.shop_type_name }}</div>
|
||||
</div>
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</template>
|
||||
<el-empty v-else description="暂无可用店铺" />
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="publishDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="publishSubmitting" @click="confirmPublish">
|
||||
确认发布
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -253,8 +296,9 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Search, Refresh, MoreFilled, Upload, Edit, Delete } from '@element-plus/icons-vue'
|
||||
import GoodsPop from '@/components/goodsPop/index.vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { fetchProductList, retryProductPublish, importProductsByExcel, deleteProduct, destroyProduct, syncGoodsFromWdt } from '@/api/product'
|
||||
import { fetchProductList, retryProductPublish, importProductsByExcel, deleteProduct, destroyProduct, syncGoodsFromWdt, releaseGoods } from '@/api/product'
|
||||
import { fetchWarehouseList } from '@/api/warehouse'
|
||||
import { fetchShopList } from '@/api/shop'
|
||||
|
||||
interface ShopListItem {
|
||||
shop_alias_name: string
|
||||
@ -305,6 +349,7 @@ export default defineComponent({
|
||||
const loading = ref<boolean>(false)
|
||||
const tableData = ref<ProductItem[]>([])
|
||||
const selectedRows = ref<ProductItem[]>([])
|
||||
const productTableRef = ref<InstanceType<typeof ElTable> | null>(null)
|
||||
const loadedOnce = ref<boolean>(false)
|
||||
|
||||
const searchParams = reactive<{
|
||||
@ -568,6 +613,153 @@ export default defineComponent({
|
||||
}
|
||||
// ========== 导入 Excel End ==========
|
||||
|
||||
// ========== 发布 / 一键发布 ==========
|
||||
const publishMode = ref<'normal' | 'oneClick'>('normal')
|
||||
const publishDialogVisible = ref(false)
|
||||
const publishLoading = ref(false)
|
||||
const publishSubmitting = ref(false)
|
||||
|
||||
interface PublishShopItem {
|
||||
shop_id: number
|
||||
shop_alias_name: string
|
||||
shop_name: string
|
||||
shop_type: number
|
||||
shop_type_name: string
|
||||
}
|
||||
|
||||
interface PublishShopGroup {
|
||||
shop_type: number
|
||||
label: string
|
||||
shops: PublishShopItem[]
|
||||
checkedIds: number[]
|
||||
}
|
||||
|
||||
const publishShopGroups = ref<PublishShopGroup[]>([])
|
||||
|
||||
const SHOP_TYPE_MAP: Record<number, string> = {
|
||||
1: '拼多多',
|
||||
2: '孔夫子',
|
||||
5: '闲鱼'
|
||||
}
|
||||
|
||||
const TARGET_SHOP_TYPES = [1, 2, 5]
|
||||
|
||||
const toggleGroupAll = (group: PublishShopGroup, val: boolean) => {
|
||||
group.checkedIds = val ? group.shops.map(s => s.shop_id) : []
|
||||
}
|
||||
|
||||
const loadPublishShops = async () => {
|
||||
publishLoading.value = true
|
||||
try {
|
||||
const res = await fetchShopList({ pageSize: 100 })
|
||||
const allShops = (res as any).list || []
|
||||
const groups: PublishShopGroup[] = TARGET_SHOP_TYPES.map(type => ({
|
||||
shop_type: type,
|
||||
label: SHOP_TYPE_MAP[type] || `类型${type}`,
|
||||
shops: [],
|
||||
checkedIds: []
|
||||
}))
|
||||
for (const shop of allShops) {
|
||||
const rawType = Number(shop.shopType ?? shop.shop_type)
|
||||
const group = groups.find(g => g.shop_type === rawType)
|
||||
if (group) {
|
||||
group.shops.push({
|
||||
shop_id: Number(shop.id || shop.shop_id),
|
||||
shop_alias_name: shop.shopAliasName || shop.shop_alias_name || shop.shopName || shop.shop_name || '',
|
||||
shop_name: shop.shopName || shop.shop_name || '',
|
||||
shop_type: rawType,
|
||||
shop_type_name: SHOP_TYPE_MAP[rawType] || ''
|
||||
})
|
||||
}
|
||||
}
|
||||
publishShopGroups.value = groups.filter(g => g.shops.length > 0)
|
||||
} catch {
|
||||
ElMessage.error({ message: '获取店铺列表失败', customClass: 'scan-error-message' })
|
||||
publishShopGroups.value = []
|
||||
} finally {
|
||||
publishLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handlePublish = () => {
|
||||
if (selectedRows.value.length === 0) {
|
||||
ElMessage.warning({ message: '请先选择要发布的商品', customClass: 'scan-error-message' })
|
||||
return
|
||||
}
|
||||
|
||||
// 检查未落位商品
|
||||
const unplacedRows = selectedRows.value.filter(r => !r.warehouse_name)
|
||||
if (unplacedRows.length > 0) {
|
||||
ElMessage.warning({
|
||||
message: `当前勾选 ${unplacedRows.length} 个未落位商品,请重新选择`,
|
||||
customClass: 'scan-error-message'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
publishMode.value = 'normal'
|
||||
publishShopGroups.value = []
|
||||
publishDialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleOneClickPublish = () => {
|
||||
publishMode.value = 'oneClick'
|
||||
publishShopGroups.value = []
|
||||
publishDialogVisible.value = true
|
||||
}
|
||||
|
||||
const confirmPublish = async () => {
|
||||
const allIds = publishShopGroups.value.flatMap(g => g.checkedIds)
|
||||
if (allIds.length === 0) {
|
||||
ElMessage.warning({ message: '请至少选择一个店铺', customClass: 'scan-error-message' })
|
||||
return
|
||||
}
|
||||
|
||||
const userInfoStr = localStorage.getItem('admin_userInfo')
|
||||
if (!userInfoStr) {
|
||||
ElMessage.error({ message: '未获取到用户信息', customClass: 'scan-error-message' })
|
||||
return
|
||||
}
|
||||
let aboutId: string
|
||||
try {
|
||||
const userInfo = JSON.parse(userInfoStr)
|
||||
aboutId = userInfo.about_id
|
||||
if (!aboutId) {
|
||||
ElMessage.error({ message: '用户信息中缺少 about_id', customClass: 'scan-error-message' })
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
ElMessage.error({ message: '用户信息解析失败', customClass: 'scan-error-message' })
|
||||
return
|
||||
}
|
||||
|
||||
const baseParams: Record<string, any> = {
|
||||
userId: Number(aboutId),
|
||||
shopIds: allIds.join(',')
|
||||
}
|
||||
if (publishMode.value === 'normal') {
|
||||
const productIds = selectedRows.value.map(r => r.id).join(',')
|
||||
baseParams.oneClick = 0
|
||||
baseParams.productIds = productIds
|
||||
} else {
|
||||
baseParams.oneClick = 1
|
||||
}
|
||||
|
||||
publishSubmitting.value = true
|
||||
try {
|
||||
await releaseGoods(baseParams)
|
||||
ElMessage.success({ message: `已成功发布到 ${allIds.length} 个店铺`, customClass: 'scan-success-message' })
|
||||
publishDialogVisible.value = false
|
||||
} catch (err: any) {
|
||||
console.error('发布失败', err)
|
||||
const msg = err?.response?.data?.msg || err?.response?.data?.message || '发布失败,请稍后重试'
|
||||
ElMessage.error({ message: msg, customClass: 'scan-error-message' })
|
||||
} finally {
|
||||
publishSubmitting.value = false
|
||||
}
|
||||
}
|
||||
// ========== 发布 End ==========
|
||||
|
||||
onMounted(() => {
|
||||
void loadProductList()
|
||||
})
|
||||
@ -583,7 +775,13 @@ export default defineComponent({
|
||||
refreshList, selectedRows, handleSelectionChange,
|
||||
// 导入
|
||||
importDialogVisible, importWarehouseId, importFile, importLoading, uploadRef, warehouseOptions,
|
||||
handleImportClick, handleFileChange, handleImportSubmit
|
||||
handleImportClick, handleFileChange, handleImportSubmit,
|
||||
// 发布 / 一键发布
|
||||
publishMode, publishDialogVisible, publishLoading, publishSubmitting, publishShopGroups,
|
||||
handleOneClickPublish, loadPublishShops, toggleGroupAll, confirmPublish,
|
||||
// 发布
|
||||
handlePublish,
|
||||
productTableRef
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -667,4 +865,79 @@ export default defineComponent({
|
||||
.shop-item {
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* ========== 一键发布弹窗 ========== */
|
||||
.publish-dialog-body {
|
||||
min-height: 200px;
|
||||
max-height: 55vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.publish-shop-group {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.publish-group-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
padding: 8px 12px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.publish-group-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.publish-group-count {
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.publish-shop-cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.publish-shop-card {
|
||||
width: 200px;
|
||||
height: auto !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.publish-shop-card :deep(.el-checkbox__input) {
|
||||
align-self: center;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.publish-shop-card :deep(.el-checkbox__label) {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.shop-card-content {
|
||||
padding: 12px 8px 12px 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.shop-card-name {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
word-break: break-all;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.shop-card-type {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
|
||||
143
src/components/warehouseSelect/index.vue
Normal file
143
src/components/warehouseSelect/index.vue
Normal file
@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div class="warehouse-select-wrapper">
|
||||
<el-select
|
||||
v-model="selectedWarehouseId"
|
||||
placeholder="选择仓库"
|
||||
clearable
|
||||
filterable
|
||||
:loading="warehouseLoading"
|
||||
style="width: 130px"
|
||||
@change="onWarehouseChange"
|
||||
@clear="onClear"
|
||||
@update:model-value="$emit('warehouseChange', $event)"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in warehouseList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select
|
||||
:model-value="modelValue"
|
||||
placeholder="选择库位"
|
||||
clearable
|
||||
filterable
|
||||
:disabled="!selectedWarehouseId"
|
||||
:loading="locationLoading"
|
||||
style="width: 150px"
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in locationList"
|
||||
:key="item.id"
|
||||
:label="item.code"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* WarehouseSelect —— 仓库→库位联动选择器
|
||||
*
|
||||
* 先选仓库,再选库位。v-model 绑定库位 ID。
|
||||
*
|
||||
* 用法:
|
||||
* <WarehouseSelect v-model="locationId" />
|
||||
*/
|
||||
import { ref, watch } from 'vue'
|
||||
import { fetchWarehouseList } from '@/api/warehouse'
|
||||
import { fetchLocationList } from '@/api/location'
|
||||
|
||||
interface WarehouseItem {
|
||||
id: number
|
||||
name: string
|
||||
code?: string
|
||||
status?: number
|
||||
}
|
||||
|
||||
interface LocationItem {
|
||||
id: number
|
||||
code: string
|
||||
name?: string
|
||||
warehouse_id?: number
|
||||
warehouse_name?: string
|
||||
status?: number
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue?: number | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: number | null]
|
||||
warehouseChange: [value: number | null]
|
||||
}>()
|
||||
|
||||
const selectedWarehouseId = ref<number | null>(null)
|
||||
const warehouseList = ref<WarehouseItem[]>([])
|
||||
const locationList = ref<LocationItem[]>([])
|
||||
const warehouseLoading = ref(false)
|
||||
const locationLoading = ref(false)
|
||||
|
||||
/** 加载仓库列表 */
|
||||
async function loadWarehouses() {
|
||||
warehouseLoading.value = true
|
||||
try {
|
||||
const res = await fetchWarehouseList({ keyword: '', page: 1, pageSize: 9999 })
|
||||
warehouseList.value = res.list || []
|
||||
} catch {
|
||||
warehouseList.value = []
|
||||
} finally {
|
||||
warehouseLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 加载指定仓库下的库位 */
|
||||
async function loadLocations(warehouseId: number) {
|
||||
locationLoading.value = true
|
||||
try {
|
||||
const res = await fetchLocationList({
|
||||
warehouseId,
|
||||
page: 1,
|
||||
pageSize: 9999,
|
||||
})
|
||||
locationList.value = res.list || []
|
||||
} catch {
|
||||
locationList.value = []
|
||||
} finally {
|
||||
locationLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function onWarehouseChange(val: number | null) {
|
||||
if (val) {
|
||||
loadLocations(val)
|
||||
// 切换仓库后清空已选库位
|
||||
emit('update:modelValue', null)
|
||||
} else {
|
||||
locationList.value = []
|
||||
emit('update:modelValue', null)
|
||||
}
|
||||
}
|
||||
|
||||
function onClear() {
|
||||
selectedWarehouseId.value = null
|
||||
locationList.value = []
|
||||
emit('update:modelValue', null)
|
||||
emit('warehouseChange', null)
|
||||
}
|
||||
|
||||
// 初始化
|
||||
loadWarehouses()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.warehouse-select-wrapper {
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
@ -237,10 +237,12 @@ async function refreshSalePrices() {
|
||||
}
|
||||
})
|
||||
|
||||
// 核价检查
|
||||
// 核价检查:从核价器配置中读取统一默认价格(元→分)
|
||||
const newPriceStr = localStorage.getItem('new_price')
|
||||
const failPrice = newPriceStr ? parseFloat(newPriceStr) * 100 : 999900
|
||||
if (list.length > 0) {
|
||||
const lastItem = list[list.length - 1]
|
||||
if (lastItem.sale_price === 999900) {
|
||||
if (lastItem.sale_price === failPrice) {
|
||||
const productId = lastItem.id
|
||||
if (productId !== lastFailedProductId) {
|
||||
isAlertShowing = true
|
||||
|
||||
@ -115,6 +115,10 @@ async function printWaybillForGroup(group, logisticsCompany = 'YUNDA') {
|
||||
})
|
||||
|
||||
console.log('createOrderBatch 返回:', createRes)
|
||||
// 报错之后需要停下来
|
||||
if (createRes?.code !== 0) {
|
||||
throw new Error(createRes?.msg || createRes?.message || '创建快递面单失败,请检查参数后重试')
|
||||
}
|
||||
|
||||
// 校验快递打印机配置
|
||||
const expressPrinter = localStorage.getItem('printer_express')
|
||||
|
||||
@ -522,7 +522,7 @@ export default {
|
||||
|
||||
try {
|
||||
// 依次登录每个已填写的账号
|
||||
const successfulAccounts: { username: string; token: string }[] = []
|
||||
const successfulAccounts: { username: string; token: string; login_name: string }[] = []
|
||||
for (let i = 0; i < accounts.value.length; i++) {
|
||||
const item = accounts.value[i]
|
||||
if (item.account && item.password) {
|
||||
@ -530,7 +530,7 @@ export default {
|
||||
const loginRes = await kongfzLogin(item.account, item.password, ip.value, port.value)
|
||||
console.log('核价器登录响应:', loginRes)
|
||||
if (loginRes?.code === 200 && loginRes?.data) {
|
||||
const { token, nickname: username } = loginRes.data
|
||||
const { token, nickname: username, mobile } = loginRes.data
|
||||
accounts.value[i].token = token
|
||||
accounts.value[i].username = username
|
||||
localStorage.setItem(username, token)
|
||||
@ -539,7 +539,7 @@ export default {
|
||||
username,
|
||||
token
|
||||
})
|
||||
successfulAccounts.push({ username, token })
|
||||
successfulAccounts.push({ username, token, login_name: mobile })
|
||||
console.log(`账号 ${item.account} 登录成功,username: ${username}`)
|
||||
} else {
|
||||
console.log(`账号 ${item.account} 登录失败:`, loginRes?.message)
|
||||
@ -605,14 +605,14 @@ export default {
|
||||
appendLoading.value = true
|
||||
|
||||
try {
|
||||
const successfulAccounts: { username: string; token: string }[] = []
|
||||
const successfulAccounts: { username: string; token: string; login_name: string }[] = []
|
||||
for (let i = 0; i < appendAccounts.value.length; i++) {
|
||||
const item = appendAccounts.value[i]
|
||||
if (item.account && item.password) {
|
||||
try {
|
||||
const loginRes = await kongfzLogin(item.account, item.password, ip.value, port.value)
|
||||
if (loginRes?.code === 200 && loginRes?.data) {
|
||||
const { token, nickname: username } = loginRes.data
|
||||
const { token, nickname: username, mobile } = loginRes.data
|
||||
appendAccounts.value[i].token = token
|
||||
appendAccounts.value[i].username = username
|
||||
localStorage.setItem(username, token)
|
||||
@ -621,7 +621,7 @@ export default {
|
||||
username,
|
||||
token
|
||||
})
|
||||
successfulAccounts.push({ username, token })
|
||||
successfulAccounts.push({ username, token, login_name: mobile })
|
||||
}
|
||||
} catch (loginError: any) {
|
||||
console.log(`追加账号 ${item.account} 登录失败:`, loginError.message)
|
||||
|
||||
@ -53,9 +53,7 @@
|
||||
<Search />
|
||||
</el-icon></template>
|
||||
</el-input>
|
||||
<el-select v-model="logParams.warehouse_id" placeholder="仓库" clearable style="width: 140px">
|
||||
<el-option v-for="item in warehouseOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
<WarehouseSelect v-model="logParams.location_id" @warehouse-change="onLogWarehouseChange" />
|
||||
<el-select v-model="logParams.change_type" placeholder="变动类型" clearable style="width: 120px">
|
||||
<el-option v-for="item in changeTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
@ -134,6 +132,7 @@ import { fetchInventoryLogList } from '@/api/inventory'
|
||||
import { fetchWarehouseList } from '@/api/warehouse'
|
||||
import InventoryByGoods from '@/components/inventory/byGoods/index.vue'
|
||||
import InventoryByLocation from '@/components/inventory/byLocation/index.vue'
|
||||
import WarehouseSelect from '@/components/warehouseSelect/index.vue'
|
||||
|
||||
/** 变动类型映射(对齐后端 change_type 枚举) */
|
||||
const CHANGE_TYPE_MAP: Record<number, { label: string; type: string }> = {
|
||||
@ -147,7 +146,7 @@ const CHANGE_TYPE_MAP: Record<number, { label: string; type: string }> = {
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Inventory',
|
||||
components: { Search, Refresh, InventoryByGoods, InventoryByLocation },
|
||||
components: { Search, Refresh, InventoryByGoods, InventoryByLocation, WarehouseSelect },
|
||||
setup() {
|
||||
const activeTab = ref<string>('summary')
|
||||
const queryMode = ref<'goods' | 'location'>('goods')
|
||||
@ -167,6 +166,7 @@ export default defineComponent({
|
||||
book_name: '',
|
||||
related_order_no: '',
|
||||
warehouse_id: null as number | null,
|
||||
location_id: null as number | null,
|
||||
change_type: null as number | null
|
||||
})
|
||||
const logDateRange = ref<string[]>([])
|
||||
@ -201,6 +201,10 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
// ==================== 变动记录 ====================
|
||||
const onLogWarehouseChange = (warehouseId: number | null) => {
|
||||
logParams.warehouse_id = warehouseId
|
||||
}
|
||||
|
||||
const loadLogList = async (): Promise<void> => {
|
||||
logLoading.value = true
|
||||
try {
|
||||
@ -210,6 +214,7 @@ export default defineComponent({
|
||||
isbn: logParams.isbn || undefined,
|
||||
book_name: logParams.book_name || undefined,
|
||||
warehouse_id: logParams.warehouse_id || undefined,
|
||||
location_id: logParams.location_id || undefined,
|
||||
change_type: logParams.change_type || undefined,
|
||||
related_order_no: logParams.related_order_no || undefined
|
||||
}
|
||||
@ -234,6 +239,7 @@ export default defineComponent({
|
||||
logParams.book_name = ''
|
||||
logParams.related_order_no = ''
|
||||
logParams.warehouse_id = null
|
||||
logParams.location_id = null
|
||||
logParams.change_type = null
|
||||
logDateRange.value = []
|
||||
logPagination.current = 1
|
||||
@ -256,6 +262,7 @@ export default defineComponent({
|
||||
warehouseOptions,
|
||||
warehouseMap,
|
||||
// 变动记录
|
||||
onLogWarehouseChange,
|
||||
logLoading,
|
||||
logList,
|
||||
logParams,
|
||||
|
||||
@ -36,9 +36,11 @@
|
||||
<el-select v-model="searchParams.status" placeholder="状态" clearable style="width: 140px">
|
||||
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<el-select v-model="searchParams.warehouse_id" placeholder="仓库" clearable style="width: 160px">
|
||||
<el-option v-for="item in warehouseOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
<WarehouseSelect
|
||||
:model-value="searchParams.location_id"
|
||||
@update:model-value="searchParams.location_id = $event"
|
||||
@warehouse-change="searchParams.warehouse_id = $event"
|
||||
/>
|
||||
<el-select v-model="searchParams.shop_type" placeholder="平台" clearable style="width: 180px">
|
||||
<el-option v-for="item in shopTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
@ -58,7 +60,7 @@
|
||||
<template #default="{ row }">
|
||||
<div v-if="detailCache[row.id]" style="padding: 12px 20px">
|
||||
<h4 style="margin: 0 0 10px; font-size: 14px; color: #303133">出库单明细</h4>
|
||||
<el-table :data="detailCache[row.id].items || []" border size="small">
|
||||
<el-table :data="getFilteredDetailItems(row.id)" border size="small">
|
||||
<el-table-column prop="product_name" label="商品名称" min-width="120" show-overflow-tooltip align="center" />
|
||||
<el-table-column prop="product_code" label="ISBN/条码" min-width="100" show-overflow-tooltip
|
||||
align="center" />
|
||||
@ -151,6 +153,11 @@
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<WarehouseSelect
|
||||
:model-value="outboundLocationId"
|
||||
@update:model-value="outboundLocationId = $event"
|
||||
@warehouse-change="outboundWarehouseId = $event"
|
||||
/>
|
||||
<span class="selected-tip">已选 <strong>{{ selectedOutboundOrders.length }}</strong> 张</span>
|
||||
</div>
|
||||
<!-- 出库单列表 -->
|
||||
@ -216,6 +223,7 @@ import {
|
||||
} from '@/api/outbound'
|
||||
import { createShippingOrder } from '@/api/shippingOrder'
|
||||
import { fetchWarehouseList } from '@/api/warehouse'
|
||||
import WarehouseSelect from '@/components/warehouseSelect/index.vue'
|
||||
/** 状态映射 */
|
||||
const STATUS_MAP: Record<number, { label: string; type: string }> = {
|
||||
1: { label: '待审核', type: 'warning' },
|
||||
@ -247,6 +255,7 @@ interface OutboundForm {
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Outbound',
|
||||
components: { WarehouseSelect },
|
||||
setup() {
|
||||
const loading = ref<boolean>(false)
|
||||
const submitLoading = ref<boolean>(false)
|
||||
@ -261,6 +270,7 @@ export default defineComponent({
|
||||
keyword: string
|
||||
status: number | null
|
||||
warehouse_id: number | null
|
||||
location_id: number | null
|
||||
shop_type: number | null
|
||||
association_order_no: string
|
||||
logistics_no: string
|
||||
@ -268,6 +278,7 @@ export default defineComponent({
|
||||
keyword: '',
|
||||
status: null,
|
||||
warehouse_id: null,
|
||||
location_id: null,
|
||||
shop_type: null,
|
||||
association_order_no: '',
|
||||
logistics_no: ''
|
||||
@ -328,6 +339,8 @@ export default defineComponent({
|
||||
const outboundOrderList = ref<any[]>([])
|
||||
const outboundLoading = ref<boolean>(false)
|
||||
const outboundTableRef = ref<any>(null)
|
||||
const outboundWarehouseId = ref<number | null>(null)
|
||||
const outboundLocationId = ref<number | null>(null)
|
||||
const outboundPagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
@ -390,6 +403,7 @@ export default defineComponent({
|
||||
out_no: searchParams.keyword || undefined,
|
||||
status: searchParams.status !== null ? String(searchParams.status) : undefined,
|
||||
warehouse_id: searchParams.warehouse_id !== null ? String(searchParams.warehouse_id) : undefined,
|
||||
location_id: searchParams.location_id || undefined,
|
||||
shop_type: searchParams.shop_type !== null ? searchParams.shop_type : undefined,
|
||||
association_order_no: searchParams.association_order_no || undefined,
|
||||
logistics_no: searchParams.logistics_no || undefined,
|
||||
@ -431,6 +445,12 @@ export default defineComponent({
|
||||
if (outboundSearchKeyword.value) {
|
||||
params.out_no = outboundSearchKeyword.value
|
||||
}
|
||||
if (outboundWarehouseId.value) {
|
||||
params.warehouse_id = String(outboundWarehouseId.value)
|
||||
}
|
||||
if (outboundLocationId.value) {
|
||||
params.location_id = String(outboundLocationId.value)
|
||||
}
|
||||
const res = await fetchOutboundList(params)
|
||||
outboundOrderList.value = res.list || []
|
||||
outboundPagination.total = res.total || 0
|
||||
@ -548,6 +568,15 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
/** 按当前搜索条件过滤明细(库位筛选时仅展示匹配库位的商品) */
|
||||
const getFilteredDetailItems = (rowId: number) => {
|
||||
const items = detailCache.value[rowId]?.items || []
|
||||
if (searchParams.location_id) {
|
||||
return items.filter((item: any) => String(item.location_id) === String(searchParams.location_id))
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
/** 添加商品明细行 */
|
||||
const addItem = (): void => {
|
||||
formData.items.push({
|
||||
@ -665,6 +694,7 @@ export default defineComponent({
|
||||
handleSizeChange,
|
||||
handleCurrentChange,
|
||||
handleExpandChange,
|
||||
getFilteredDetailItems,
|
||||
addItem,
|
||||
removeItem,
|
||||
handleApprove,
|
||||
@ -677,6 +707,8 @@ export default defineComponent({
|
||||
outboundOrderList,
|
||||
outboundLoading,
|
||||
outboundTableRef,
|
||||
outboundWarehouseId,
|
||||
outboundLocationId,
|
||||
formatAmount,
|
||||
handleOutboundSearch,
|
||||
handleOutboundSelectionChange,
|
||||
|
||||
@ -15,10 +15,11 @@
|
||||
<el-select v-model="searchParams.status" placeholder="状态" clearable style="width: 140px">
|
||||
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<el-select v-model="searchParams.warehouse_id" placeholder="仓库" clearable filterable remote reserve-keyword
|
||||
:remote-method="loadWarehouses" :loading="warehouseLoading" style="width: 160px">
|
||||
<el-option v-for="item in warehouseOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
<WarehouseSelect
|
||||
:model-value="searchParams.location_id"
|
||||
@update:model-value="searchParams.location_id = $event"
|
||||
@warehouse-change="searchParams.warehouse_id = $event"
|
||||
/>
|
||||
<el-select v-model="searchParams.shop_type" placeholder="平台" clearable style="width: 180px">
|
||||
<el-option label="闲鱼" :value="5" />
|
||||
<el-option label="孔夫子" :value="2" />
|
||||
@ -95,6 +96,9 @@
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="仓库" min-width="100" align="center">
|
||||
<template #default="{ row }">{{ row.warehouse_name || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="库位" min-width="100" align="center">
|
||||
<template #default="{ row }">{{ row.items?.[0]?.location_code || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="店铺/类型" min-width="140" align="center">
|
||||
@ -200,6 +204,7 @@ import { useRoute } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
// added by copilot: useRoute for query params
|
||||
import { Search, Refresh, Plus, Edit, Delete, View, Check } from '@element-plus/icons-vue'
|
||||
import WarehouseSelect from '@/components/warehouseSelect/index.vue'
|
||||
import dayjs from 'dayjs'
|
||||
import {
|
||||
fetchSalesOrderList,
|
||||
@ -242,6 +247,7 @@ interface SalesOrderForm {
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SalesOrder',
|
||||
components: { WarehouseSelect },
|
||||
setup() {
|
||||
const loading = ref<boolean>(false)
|
||||
const submitLoading = ref<boolean>(false)
|
||||
@ -256,6 +262,7 @@ export default defineComponent({
|
||||
keyword: string
|
||||
status: number | null
|
||||
warehouse_id: number | null
|
||||
location_id: number | null
|
||||
shop_type: number | null
|
||||
association_order_no: string
|
||||
logistics_no: string
|
||||
@ -263,6 +270,7 @@ export default defineComponent({
|
||||
keyword: '',
|
||||
status: null,
|
||||
warehouse_id: null,
|
||||
location_id: null,
|
||||
shop_type: null,
|
||||
association_order_no: '',
|
||||
logistics_no: ''
|
||||
@ -377,6 +385,10 @@ export default defineComponent({
|
||||
const res = await fetchSalesOrderDetails({
|
||||
page: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
warehouse_id: searchParams.warehouse_id || undefined,
|
||||
location_id: searchParams.location_id || undefined,
|
||||
status: searchParams.status || undefined,
|
||||
keyword: searchParams.keyword,
|
||||
shop_type: searchParams.shop_type !== null ? searchParams.shop_type : undefined,
|
||||
association_order_no: searchParams.association_order_no,
|
||||
logistics_no: searchParams.logistics_no
|
||||
@ -402,6 +414,7 @@ export default defineComponent({
|
||||
searchParams.keyword = ''
|
||||
searchParams.status = null
|
||||
searchParams.warehouse_id = null
|
||||
searchParams.location_id = null
|
||||
searchParams.shop_type = null
|
||||
searchParams.association_order_no = ''
|
||||
searchParams.logistics_no = ''
|
||||
@ -585,8 +598,6 @@ export default defineComponent({
|
||||
formRef,
|
||||
formData,
|
||||
formRules,
|
||||
warehouseOptions,
|
||||
warehouseLoading,
|
||||
loadWarehouses,
|
||||
warehouseMap,
|
||||
customerMap,
|
||||
|
||||
@ -37,7 +37,11 @@
|
||||
<el-select v-model="searchParams.status" placeholder="状态" clearable style="width: 140px">
|
||||
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<LocationSelect v-model="searchParams.warehouse_id" style="width: 160px" />
|
||||
<WarehouseSelect
|
||||
:model-value="searchParams.location_id"
|
||||
@update:model-value="searchParams.location_id = $event"
|
||||
@warehouse-change="searchParams.warehouse_id = $event"
|
||||
/>
|
||||
<el-select v-model="searchParams.shop_type" placeholder="平台" clearable style="width: 180px">
|
||||
<el-option label="闲鱼" :value="5" />
|
||||
<el-option label="孔夫子" :value="2" />
|
||||
@ -124,6 +128,9 @@
|
||||
<el-table-column label="仓库" min-width="120" align="center">
|
||||
<template #default="{ row }">{{ warehouseMap[row.warehouse_id] || row.warehouse_id || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="库位" min-width="120" align="center">
|
||||
<template #default="{ row }">{{ row.location_code || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="订单日期" min-width="110" align="center">
|
||||
<template #default="{ row }">{{ formatDate(row.order_date) }}</template>
|
||||
</el-table-column>
|
||||
@ -212,7 +219,7 @@
|
||||
@close="outboundGenerated = false">
|
||||
<!-- 搜索区 -->
|
||||
<div class="outbound-search-bar">
|
||||
<el-input v-model="outboundSearchKeyword" placeholder="搜索销售订单号 / 平台 / 仓库" clearable style="width: 320px"
|
||||
<el-input v-model="outboundSearchKeyword" placeholder="搜索销售订单号 / 平台" clearable style="width: 280px"
|
||||
@input="handleOutboundSearch" @keyup.enter="handleOutboundSearch">
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
@ -220,6 +227,11 @@
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<WarehouseSelect
|
||||
:model-value="outboundLocationId"
|
||||
@update:model-value="outboundLocationId = $event"
|
||||
@warehouse-change="outboundWarehouseId = $event"
|
||||
/>
|
||||
<span class="selected-tip">已选 <strong>{{ selectedOutboundOrders.length }}</strong> 张</span>
|
||||
</div>
|
||||
|
||||
@ -282,7 +294,7 @@ import { useRoute } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
// added by copilot: useRoute for query params
|
||||
import { Search, Refresh, Plus, Edit, Delete, View, Loading, Check } from '@element-plus/icons-vue'
|
||||
import LocationSelect from '@/components/locationSelect/index.vue'
|
||||
import WarehouseSelect from '@/components/warehouseSelect/index.vue'
|
||||
import dayjs from 'dayjs'
|
||||
import {
|
||||
fetchSalesOrderList,
|
||||
@ -330,7 +342,7 @@ interface SalesOrderForm {
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SalesOrder',
|
||||
components: { LocationSelect },
|
||||
components: { WarehouseSelect },
|
||||
setup() {
|
||||
const loading = ref<boolean>(false)
|
||||
const submitLoading = ref<boolean>(false)
|
||||
@ -345,6 +357,7 @@ export default defineComponent({
|
||||
keyword: string
|
||||
status: number | null
|
||||
warehouse_id: number | null
|
||||
location_id: number | null
|
||||
shop_type: number | null
|
||||
association_order_no: string
|
||||
logistics_no: string
|
||||
@ -352,6 +365,7 @@ export default defineComponent({
|
||||
keyword: '',
|
||||
status: null,
|
||||
warehouse_id: null,
|
||||
location_id: null,
|
||||
shop_type: null,
|
||||
association_order_no: '',
|
||||
logistics_no: ''
|
||||
@ -403,6 +417,8 @@ export default defineComponent({
|
||||
const generateLoading = ref<boolean>(false)
|
||||
const outboundOrderList = ref<any[]>([])
|
||||
const outboundSearchKeyword = ref<string>('')
|
||||
const outboundWarehouseId = ref<number | null>(null)
|
||||
const outboundLocationId = ref<number | null>(null)
|
||||
const selectedOutboundOrders = ref<any[]>([])
|
||||
const outboundTableRef = ref<any>(null)
|
||||
const outboundRemark = ref('')
|
||||
@ -467,6 +483,7 @@ export default defineComponent({
|
||||
keyword: searchParams.keyword,
|
||||
status: searchParams.status || undefined,
|
||||
warehouse_id: searchParams.warehouse_id || undefined,
|
||||
location_id: searchParams.location_id || undefined,
|
||||
shop_type: searchParams.shop_type !== null ? searchParams.shop_type : undefined,
|
||||
association_order_no: searchParams.association_order_no || undefined,
|
||||
logistics_no: searchParams.logistics_no || undefined,
|
||||
@ -496,6 +513,7 @@ export default defineComponent({
|
||||
searchParams.keyword = ''
|
||||
searchParams.status = null
|
||||
searchParams.warehouse_id = null
|
||||
searchParams.location_id = null
|
||||
searchParams.shop_type = null
|
||||
searchParams.association_order_no = ''
|
||||
searchParams.logistics_no = ''
|
||||
@ -577,12 +595,16 @@ export default defineComponent({
|
||||
outboundDialogVisible.value = true
|
||||
outboundGenerated.value = false
|
||||
outboundSearchKeyword.value = ''
|
||||
outboundWarehouseId.value = null
|
||||
outboundLocationId.value = null
|
||||
selectedOutboundOrders.value = []
|
||||
outboundLoading.value = true
|
||||
try {
|
||||
const res = await fetchSalesOrderList({
|
||||
keyword: '',
|
||||
status: 3, // 只加载已确认状态的订单
|
||||
warehouse_id: outboundWarehouseId.value || undefined,
|
||||
location_id: outboundLocationId.value || undefined,
|
||||
page: 1,
|
||||
pageSize: 100
|
||||
})
|
||||
@ -602,6 +624,8 @@ export default defineComponent({
|
||||
const res = await fetchSalesOrderList({
|
||||
keyword: outboundSearchKeyword.value,
|
||||
status: 3,
|
||||
warehouse_id: outboundWarehouseId.value || undefined,
|
||||
location_id: outboundLocationId.value || undefined,
|
||||
page: 1,
|
||||
pageSize: 100
|
||||
})
|
||||
@ -723,6 +747,8 @@ export default defineComponent({
|
||||
outboundLoading,
|
||||
outboundOrderList,
|
||||
outboundSearchKeyword,
|
||||
outboundWarehouseId,
|
||||
outboundLocationId,
|
||||
selectedOutboundOrders,
|
||||
outboundTableRef,
|
||||
outboundRemark,
|
||||
|
||||
@ -152,9 +152,11 @@
|
||||
<el-select v-model="searchParams.status" placeholder="状态" clearable style="width: 120px">
|
||||
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<el-select v-model="searchParams.warehouse_id" placeholder="仓库" clearable style="width: 140px">
|
||||
<el-option v-for="item in warehouseOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
<WarehouseSelect
|
||||
:model-value="searchParams.location_id"
|
||||
@update:model-value="searchParams.location_id = $event"
|
||||
@warehouse-change="searchParams.warehouse_id = $event"
|
||||
/>
|
||||
<el-select v-model="searchParams.shop_type" placeholder="平台" clearable style="width: 160px">
|
||||
<el-option label="闲鱼" :value="5" />
|
||||
<el-option label="孔夫子" :value="2" />
|
||||
@ -177,7 +179,7 @@
|
||||
<template #default="{ row }">
|
||||
<div v-if="detailCache[row.id]" style="padding: 12px 20px">
|
||||
<h4 style="margin: 0 0 10px; font-size: 14px; color: #303133">发货明细</h4>
|
||||
<el-table :data="detailCache[row.id].items || []" border size="small"
|
||||
<el-table :data="getFilteredDetailItems(row.id)" border size="small"
|
||||
@row-click="handleDetailItemClick">
|
||||
<el-table-column prop="product_name" label="商品名称" min-width="200" show-overflow-tooltip
|
||||
align="center" />
|
||||
@ -412,6 +414,7 @@ import { copyToClipboard } from '@/utils/clipboard'
|
||||
import { fetchShippingOrderList, fetchShippingOrderDetail, updateShippingOrderLogistics, fetchFastMailList, fetchExpressInfo, createBmOrderDaYin, recycleWaybillNo } from '@/api/shippingOrder'
|
||||
import { createPrintTask } from '@/api/print'
|
||||
import { fetchWarehouseList } from '@/api/warehouse'
|
||||
import WarehouseSelect from '@/components/warehouseSelect/index.vue'
|
||||
import { getBookDetails } from '@/api/product'
|
||||
import { executeMergePrint } from '@/utils/printFlow/printFlow'
|
||||
|
||||
@ -425,6 +428,7 @@ const STATUS_MAP: Record<number, { label: string; type: string }> = {
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ShippingOrder',
|
||||
components: { WarehouseSelect },
|
||||
setup() {
|
||||
const router = useRouter()
|
||||
const loading = ref<boolean>(false)
|
||||
@ -477,13 +481,15 @@ export default defineComponent({
|
||||
keyword: string
|
||||
status: number | null
|
||||
warehouse_id: number | null
|
||||
location_id: number | null
|
||||
shop_type: number | null
|
||||
association_order_no: string
|
||||
logistics_no: string
|
||||
}>({
|
||||
keyword: '',
|
||||
status: 1, // 默认选中"待发货"
|
||||
status: 1,
|
||||
warehouse_id: null,
|
||||
location_id: null,
|
||||
shop_type: null,
|
||||
association_order_no: '',
|
||||
logistics_no: ''
|
||||
@ -1109,6 +1115,7 @@ export default defineComponent({
|
||||
check_no: searchParams.keyword,
|
||||
status: searchParams.status || undefined,
|
||||
warehouse_id: searchParams.warehouse_id || undefined,
|
||||
location_id: searchParams.location_id || undefined,
|
||||
shop_type: searchParams.shop_type !== null ? searchParams.shop_type : undefined,
|
||||
association_order_no: searchParams.association_order_no || undefined,
|
||||
logistics_no: searchParams.logistics_no || undefined,
|
||||
@ -1136,6 +1143,7 @@ export default defineComponent({
|
||||
searchParams.keyword = ''
|
||||
searchParams.status = 1
|
||||
searchParams.warehouse_id = null
|
||||
searchParams.location_id = null
|
||||
searchParams.shop_type = null
|
||||
searchParams.association_order_no = ''
|
||||
searchParams.logistics_no = ''
|
||||
@ -1168,6 +1176,15 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
/** 按当前搜索条件过滤明细(库位筛选时仅展示匹配库位的商品) */
|
||||
const getFilteredDetailItems = (rowId: number) => {
|
||||
const items = detailCache.value[rowId]?.items || []
|
||||
if (searchParams.location_id) {
|
||||
return items.filter((item: any) => String(item.location_id) === String(searchParams.location_id))
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const route = useRoute()
|
||||
if (route.query.keyword) {
|
||||
@ -1215,6 +1232,7 @@ export default defineComponent({
|
||||
handleSizeChange,
|
||||
handleCurrentChange,
|
||||
handleExpandChange,
|
||||
getFilteredDetailItems,
|
||||
navigateToSalesOrder,
|
||||
navigateToOutbound,
|
||||
copyToClipboard,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user