feat: 添加参考价格列 + 修复公司筛选跨域问题 + CLIP模型持久化
## 新增功能 - material_base 表新增 reference_price 列(NUMERIC(10,2)) - 基础信息 list.vue / buyOdoo.vue 页面增加「参考价格」列展示和编辑 - 新增 material_list:referencePrice 权限元素,支持按角色控制可见性 ## Bug 修复 - 入库三页面(buy/product/semi)公司筛选:非超管用户 company=ALL 不再传给旧版后端 - 基础信息两页面(list/buyOdoo):buyOdoo 增加 v-if=isSuperAdmin 与 list.vue 行为统一 - company=ALL 默认值在各页面 getList/fetchData 中自动过滤,兼容旧版后端 ## 运维优化 - docker-compose.prod.yml 增加 models_prod 卷挂载,CLIP模型持久化免重复上传 - deploy_code.sh 增加 models_prod 目录检查与模型文件存在性告警
This commit is contained in:
9
deploy_code.sh
Executable file → Normal file
9
deploy_code.sh
Executable file → Normal file
@ -16,9 +16,16 @@ echo "[1/4] 正在服务器上急救环境并备份旧代码 (可能需要输入
|
||||
ssh -t $SERVER "sudo mkdir -p $REMOTE_BACKUP_DIR && \
|
||||
cd $REMOTE_DIR && \
|
||||
echo '>> 检查并修复缺失目录 (防止 tar 崩溃)...' && \
|
||||
sudo mkdir -p inventory-backend inventory-web && \
|
||||
sudo mkdir -p inventory-backend inventory-web models_prod && \
|
||||
echo '>> 执行代码备份...' && \
|
||||
sudo tar -czf $REMOTE_BACKUP_DIR/code_backup.tar.gz inventory-backend inventory-web docker-compose.prod.yml && \
|
||||
echo '>> 检查 CLIP 模型文件...' && \
|
||||
if [ ! -f models_prod/clip_vision.onnx ]; then \
|
||||
echo '⚠️ 警告: models_prod/clip_vision.onnx 不存在!拍照识图功能将无法使用。'; \
|
||||
echo '⚠️ 请手动上传: scp inventory-backend/models/clip_vision.onnx ${SERVER}:${REMOTE_DIR}/models_prod/'; \
|
||||
else \
|
||||
echo '✅ CLIP 模型文件已存在'; \
|
||||
fi && \
|
||||
echo '>> 执行清理:仅保留 data_copy 下最新的 2 个备份...' && \
|
||||
cd $REMOTE_BACKUP_BASE && \
|
||||
sudo sh -c 'ls -dt */ 2>/dev/null | tail -n +3 | xargs -I {} rm -rf {} || true'"
|
||||
|
||||
@ -25,6 +25,7 @@ services:
|
||||
- "8000"
|
||||
volumes:
|
||||
- ./uploads_prod:/app/uploads
|
||||
- ./models_prod:/app/models
|
||||
command: gunicorn -c gunicorn.conf.py run:app
|
||||
environment:
|
||||
DATABASE_URL: postgresql://prod_user:StrongPassword123!@db:5432/inventory_system
|
||||
|
||||
@ -43,6 +43,7 @@ def get_current_user_permissions():
|
||||
'material_list:availableCount',
|
||||
'material_list:files',
|
||||
'material_list:isEnabled',
|
||||
'material_list:referencePrice',
|
||||
'material_list:operation'
|
||||
]
|
||||
perm_dict = AuthService.get_user_permissions(user_role, company_name=user_company)
|
||||
@ -72,6 +73,7 @@ def filter_item_by_permissions(item_dict, user_permissions):
|
||||
'availableCount': 'material_list:availableCount',
|
||||
'generalManual': 'material_list:files',
|
||||
'generalImage': 'material_list:files',
|
||||
'referencePrice': 'material_list:referencePrice',
|
||||
'isEnabled': 'material_list:isEnabled'
|
||||
}
|
||||
for field, perm_code in field_to_perm.items():
|
||||
@ -252,6 +254,7 @@ def create():
|
||||
'availableCount': 'material_list:availableCount',
|
||||
'generalManual': 'material_list:files',
|
||||
'generalImage': 'material_list:files',
|
||||
'referencePrice': 'material_list:referencePrice',
|
||||
'isEnabled': 'material_list:isEnabled'
|
||||
}
|
||||
# 过滤用户没有权限的字段
|
||||
@ -311,6 +314,7 @@ def update(id):
|
||||
'availableCount': 'material_list:availableCount',
|
||||
'generalManual': 'material_list:files',
|
||||
'generalImage': 'material_list:files',
|
||||
'referencePrice': 'material_list:referencePrice',
|
||||
'isEnabled': 'material_list:isEnabled'
|
||||
}
|
||||
# 过滤用户没有权限的字段
|
||||
|
||||
@ -39,6 +39,9 @@ class MaterialBase(db.Model):
|
||||
# 强制质检标记(采购入库时必须上传检测报告)
|
||||
is_inspection_required = db.Column(db.Boolean, default=False, comment='是否强制要求质检')
|
||||
|
||||
# 参考价格
|
||||
reference_price = db.Column(db.Numeric(10, 2), nullable=True, comment='参考价格')
|
||||
|
||||
# CLIP 视觉向量(用于以图搜图)
|
||||
img_embedding = db.Column(Vector(512), nullable=True)
|
||||
|
||||
@ -96,6 +99,8 @@ class MaterialBase(db.Model):
|
||||
'isEnabled': bool(self.is_enabled),
|
||||
# 强制质检标记
|
||||
'isInspectionRequired': bool(self.is_inspection_required),
|
||||
# 参考价格
|
||||
'referencePrice': float(self.reference_price) if self.reference_price is not None else None,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -259,6 +259,7 @@ class MaterialBaseService:
|
||||
'type': 'material_type',
|
||||
'spec': 'spec_model',
|
||||
'unit': 'unit',
|
||||
'referencePrice': 'reference_price',
|
||||
'inventoryCount': 'total_inv',
|
||||
'availableCount': 'total_avail'
|
||||
}
|
||||
@ -273,6 +274,7 @@ class MaterialBaseService:
|
||||
'type': 'material_list:type',
|
||||
'spec': 'material_list:spec',
|
||||
'unit': 'material_list:unit',
|
||||
'referencePrice': 'material_list:referencePrice',
|
||||
'inventoryCount': 'material_list:inventoryCount',
|
||||
'availableCount': 'material_list:availableCount'
|
||||
}
|
||||
@ -390,6 +392,7 @@ class MaterialBaseService:
|
||||
'type': MaterialBase.material_type,
|
||||
'spec': MaterialBase.spec_model,
|
||||
'unit': MaterialBase.unit,
|
||||
'referencePrice': MaterialBase.reference_price,
|
||||
'inventoryCount': inner_sub.c.total_inv,
|
||||
'availableCount': inner_sub.c.total_avail
|
||||
}
|
||||
@ -556,6 +559,7 @@ class MaterialBaseService:
|
||||
product_image=json.dumps(data.get('generalImage', [])),
|
||||
product_image_remark=data.get('productImageRemark', ''),
|
||||
manual_link_remark=data.get('manualLinkRemark', ''),
|
||||
reference_price=data.get('referencePrice'),
|
||||
is_enabled=is_enabled_val
|
||||
)
|
||||
db.session.add(new_material)
|
||||
@ -625,6 +629,9 @@ class MaterialBaseService:
|
||||
)
|
||||
|
||||
# 【核心修改】:兼容前端传来的布尔值
|
||||
if 'referencePrice' in data:
|
||||
material.reference_price = data['referencePrice']
|
||||
|
||||
if 'isEnabled' in data:
|
||||
raw_enabled = data['isEnabled']
|
||||
material.is_enabled = str(raw_enabled).lower() in ['1', 'true', 'yes', 't']
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
</el-input>
|
||||
|
||||
<el-select
|
||||
v-if="isSuperAdmin"
|
||||
v-model="queryParams.company"
|
||||
placeholder="所属公司"
|
||||
clearable
|
||||
@ -29,6 +30,7 @@
|
||||
style="width: 120px; margin-right: 10px;"
|
||||
@change="handleQuery"
|
||||
>
|
||||
<el-option label="全部 (跨域)" value="ALL" />
|
||||
<el-option v-for="item in companyOptions" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
|
||||
@ -194,6 +196,7 @@
|
||||
<el-checkbox v-if="hasColPermission('files')" v-model="columns.files.visible" label="资料" />
|
||||
<el-checkbox v-if="hasColPermission('isEnabled')" v-model="columns.isEnabled.visible" label="状态" />
|
||||
<el-checkbox v-if="hasColPermission('isInspectionRequired')" v-model="columns.isInspectionRequired.visible" label="强制质检" />
|
||||
<el-checkbox v-if="hasColPermission('referencePrice')" v-model="columns.referencePrice.visible" label="参考价格" />
|
||||
<el-checkbox v-if="hasColPermission('warningStatus')" v-model="columns.warningStatus.visible" label="预警状态" />
|
||||
</div>
|
||||
</el-popover>
|
||||
@ -330,6 +333,12 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="columns.referencePrice.visible" prop="referencePrice" label="参考价格" min-width="120" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.referencePrice != null" class="money-text">{{ scope.row.referencePrice?.toFixed(2) }}</span>
|
||||
<span v-else style="color: #ccc;">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="columns.warningStatus.visible" label="预警状态" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<template v-if="row.warningStatus === 2">
|
||||
@ -491,6 +500,14 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="参考价格" prop="referencePrice" v-if="hasFieldPermission('referencePrice')">
|
||||
<el-input-number v-model="form.referencePrice" :precision="2" :min="0" controls-position="right" style="width: 100%" placeholder="请输入参考价格" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="产品图" prop="generalImage" v-if="hasFieldPermission('files')">
|
||||
<div class="upload-container" id="upload-generalImage">
|
||||
<el-upload
|
||||
@ -701,6 +718,7 @@ import ImageSearchDialog from '@/components/ImageSearchDialog.vue';
|
||||
import { imageSearch as imageSearchApi, type ImageSearchItem } from '@/api/common/upload';
|
||||
|
||||
const userStore = useUserStore();
|
||||
const isSuperAdmin = computed(() => userStore.role === 'SUPER_ADMIN');
|
||||
|
||||
// --- 类型定义 ---
|
||||
interface MaterialBaseVO {
|
||||
@ -723,6 +741,7 @@ interface MaterialBaseVO {
|
||||
warningOrdered?: boolean;
|
||||
warningRedEmails?: string;
|
||||
warningYellowEmails?: string;
|
||||
referencePrice?: number;
|
||||
}
|
||||
|
||||
interface QueryParams {
|
||||
@ -770,7 +789,8 @@ const fieldOptions = computed(() => {
|
||||
{ value: 'spec', label: '规格型号', perm: 'material_list:spec' },
|
||||
{ value: 'unit', label: '单位', perm: 'material_list:unit' },
|
||||
{ value: 'inventoryCount', label: '库存数', perm: 'material_list:inventoryCount' },
|
||||
{ value: 'availableCount', label: '可用数', perm: 'material_list:availableCount' }
|
||||
{ value: 'availableCount', label: '可用数', perm: 'material_list:availableCount' },
|
||||
{ value: 'referencePrice', label: '参考价格', perm: 'material_list:referencePrice' }
|
||||
];
|
||||
return allFields.filter(item => userStore.hasPermission(item.perm));
|
||||
});
|
||||
@ -803,7 +823,7 @@ const queryParams = reactive<QueryParams>({
|
||||
searchField: 'all',
|
||||
category: '',
|
||||
type: '',
|
||||
company: '',
|
||||
company: 'ALL',
|
||||
isEnabled: undefined,
|
||||
orderByColumn: '',
|
||||
isAsc: undefined,
|
||||
@ -925,7 +945,7 @@ const columns = reactive({
|
||||
commonName: { visible: true }, category: { visible: true }, type: { visible: true },
|
||||
spec: { visible: true }, unit: { visible: true }, inventory: { visible: true },
|
||||
available: { visible: true }, files: { visible: true }, isEnabled: { visible: true },
|
||||
isInspectionRequired: { visible: true }, warningStatus: { visible: true }
|
||||
isInspectionRequired: { visible: true }, referencePrice: { visible: true }, warningStatus: { visible: true }
|
||||
});
|
||||
|
||||
const permissionMap: Record<string, string> = {
|
||||
@ -933,7 +953,8 @@ const permissionMap: Record<string, string> = {
|
||||
commonName: 'material_list:commonName', category: 'material_list:category', type: 'material_list:type',
|
||||
spec: 'material_list:spec', unit: 'material_list:unit', inventory: 'material_list:inventoryCount',
|
||||
available: 'material_list:availableCount', files: 'material_list:files', isEnabled: 'material_list:isEnabled',
|
||||
isInspectionRequired: 'material_list:operation', warningStatus: 'material_list:view_warning'
|
||||
isInspectionRequired: 'material_list:operation', referencePrice: 'material_list:referencePrice',
|
||||
warningStatus: 'material_list:view_warning'
|
||||
};
|
||||
|
||||
const getStorageKey = () => `MOM_BASIC_INFO_COLS_${userStore.username || 'DEFAULT'}`;
|
||||
@ -1024,6 +1045,7 @@ const formRef = ref<FormInstance>();
|
||||
const initForm = {
|
||||
id: undefined, companyName: '', name: '', commonName: '', category: '', type: '', spec: '', unit: '',
|
||||
visibilityLevel: 0, generalManual: [] as string[], generalImage: [] as string[], isEnabled: true,
|
||||
referencePrice: undefined as number | undefined,
|
||||
productImageRemark: '', manualLinkRemark: ''
|
||||
};
|
||||
const form = ref({...initForm});
|
||||
@ -1096,6 +1118,10 @@ const getList = () => {
|
||||
...queryParams,
|
||||
advancedFilters: JSON.stringify(queryParams.advancedFilters || [])
|
||||
};
|
||||
// 兼容旧版后端:company=ALL 时不传过滤参数
|
||||
if (params.company === 'ALL') {
|
||||
delete params.company
|
||||
}
|
||||
listMaterialBase(params).then((response: any) => {
|
||||
if (response && response.data) {
|
||||
tableData.value = response.data.items;
|
||||
@ -1133,7 +1159,7 @@ const handleInputSearch = () => {
|
||||
};
|
||||
|
||||
const handleSortChange = ({ column, prop, order }: any) => {
|
||||
const sortableColumns = ['inventoryCount', 'availableCount', 'companyName', 'name', 'commonName', 'category', 'type', 'spec', 'unit'];
|
||||
const sortableColumns = ['inventoryCount', 'availableCount', 'referencePrice', 'companyName', 'name', 'commonName', 'category', 'type', 'spec', 'unit'];
|
||||
if (prop && sortableColumns.includes(prop)) {
|
||||
queryParams.orderByColumn = prop;
|
||||
queryParams.isAsc = order === 'ascending' ? 'asc' : order === 'descending' ? 'desc' : undefined;
|
||||
@ -1147,7 +1173,7 @@ const handleSortChange = ({ column, prop, order }: any) => {
|
||||
const handleQuery = () => { getList(); };
|
||||
const resetQuery = () => {
|
||||
queryParams.keyword = ''; queryParams.searchField = 'all'; queryParams.category = '';
|
||||
queryParams.type = ''; queryParams.company = ''; queryParams.isEnabled = undefined;
|
||||
queryParams.type = ''; queryParams.company = isSuperAdmin.value ? 'ALL' : ''; queryParams.isEnabled = undefined;
|
||||
queryParams.orderByColumn = ''; queryParams.isAsc = undefined; queryParams.has_stock = '';
|
||||
selectedItems.value = []; groupSelections.value = {};
|
||||
Object.values(tableRefs.value).forEach(t => t?.clearSelection?.());
|
||||
|
||||
@ -194,6 +194,7 @@
|
||||
<el-checkbox v-if="hasColPermission('files')" v-model="columns.files.visible" label="资料" />
|
||||
<el-checkbox v-if="hasColPermission('isEnabled')" v-model="columns.isEnabled.visible" label="状态" />
|
||||
<el-checkbox v-if="hasColPermission('isInspectionRequired')" v-model="columns.isInspectionRequired.visible" label="强制质检" />
|
||||
<el-checkbox v-if="hasColPermission('referencePrice')" v-model="columns.referencePrice.visible" label="参考价格" />
|
||||
<el-checkbox v-if="hasColPermission('warningStatus')" v-model="columns.warningStatus.visible" label="预警状态" />
|
||||
</div>
|
||||
</el-popover>
|
||||
@ -317,6 +318,12 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="columns.referencePrice.visible" prop="referencePrice" label="参考价格" min-width="120" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.referencePrice != null" class="money-text">{{ scope.row.referencePrice?.toFixed(2) }}</span>
|
||||
<span v-else style="color: #ccc;">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="columns.warningStatus.visible" label="预警状态" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<template v-if="row.warningStatus === 2">
|
||||
@ -488,6 +495,14 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="参考价格" prop="referencePrice" v-if="hasFieldPermission('referencePrice')">
|
||||
<el-input-number v-model="form.referencePrice" :precision="2" :min="0" controls-position="right" style="width: 100%" placeholder="请输入参考价格" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="产品图" prop="generalImage" v-if="hasFieldPermission('files')">
|
||||
<div class="upload-container" id="upload-generalImage">
|
||||
<el-upload
|
||||
@ -724,6 +739,7 @@ interface MaterialBaseVO {
|
||||
warningOrdered?: boolean;
|
||||
warningRedEmails?: string;
|
||||
warningYellowEmails?: string;
|
||||
referencePrice?: number;
|
||||
}
|
||||
|
||||
interface QueryParams {
|
||||
@ -773,7 +789,8 @@ const fieldOptions = computed(() => {
|
||||
{ value: 'spec', label: '规格型号', perm: 'material_list:spec' },
|
||||
{ value: 'unit', label: '单位', perm: 'material_list:unit' },
|
||||
{ value: 'inventoryCount', label: '库存数', perm: 'material_list:inventoryCount' },
|
||||
{ value: 'availableCount', label: '可用数', perm: 'material_list:availableCount' }
|
||||
{ value: 'availableCount', label: '可用数', perm: 'material_list:availableCount' },
|
||||
{ value: 'referencePrice', label: '参考价格', perm: 'material_list:referencePrice' }
|
||||
];
|
||||
// 根据用户权限过滤
|
||||
return allFields.filter(item => userStore.hasPermission(item.perm));
|
||||
@ -916,6 +933,7 @@ const columns = reactive({
|
||||
files: { visible: true },
|
||||
isEnabled: { visible: true },
|
||||
isInspectionRequired: { visible: true },
|
||||
referencePrice: { visible: true },
|
||||
warningStatus: { visible: true }
|
||||
});
|
||||
|
||||
@ -934,6 +952,7 @@ const permissionMap: Record<string, string> = {
|
||||
files: 'material_list:files',
|
||||
isEnabled: 'material_list:isEnabled',
|
||||
isInspectionRequired: 'material_list:operation',
|
||||
referencePrice: 'material_list:referencePrice',
|
||||
warningStatus: 'material_list:view_warning'
|
||||
};
|
||||
|
||||
@ -1103,6 +1122,7 @@ const initForm = {
|
||||
generalManual: [] as string[],
|
||||
generalImage: [] as string[],
|
||||
isEnabled: true, // 已修改为默认 true
|
||||
referencePrice: undefined as number | undefined,
|
||||
productImageRemark: '',
|
||||
manualLinkRemark: '',
|
||||
};
|
||||
@ -1204,6 +1224,10 @@ const getList = () => {
|
||||
...queryParams,
|
||||
advancedFilters: JSON.stringify(queryParams.advancedFilters || [])
|
||||
};
|
||||
// 兼容旧版后端:company=ALL 时不传过滤参数
|
||||
if (params.company === 'ALL') {
|
||||
delete params.company
|
||||
}
|
||||
listMaterialBase(params)
|
||||
.then((response: any) => {
|
||||
if (response && response.data) {
|
||||
@ -1277,7 +1301,7 @@ const handleInputSearch = () => {
|
||||
};
|
||||
|
||||
const handleSortChange = ({ column, prop, order }: any) => {
|
||||
const sortableColumns = ['inventoryCount', 'availableCount', 'companyName', 'name', 'commonName', 'category', 'type', 'spec', 'unit'];
|
||||
const sortableColumns = ['inventoryCount', 'availableCount', 'referencePrice', 'companyName', 'name', 'commonName', 'category', 'type', 'spec', 'unit'];
|
||||
if (prop && sortableColumns.includes(prop)) {
|
||||
queryParams.orderByColumn = prop;
|
||||
queryParams.isAsc = order === 'ascending' ? 'asc' : order === 'descending' ? 'desc' : undefined;
|
||||
|
||||
@ -231,8 +231,8 @@
|
||||
</el-link>
|
||||
</template>
|
||||
|
||||
<template #default="scope" v-else-if="['unit_price', 'post_tax_unit_price', 'total_price'].includes(col.prop)">
|
||||
<span class="money-text">{{ formatMoney(scope.row[col.prop], scope.row.currency) }}</span>
|
||||
<template #default="scope" v-else-if="['unit_price', 'total_price'].includes(col.prop)">
|
||||
<span class="money-text">{{ formatMoney(scope.row[col.prop]) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
@ -767,7 +767,6 @@ const hasFormFieldPermission = (fieldName: string) => {
|
||||
available_quantity: 'inbound_buy:available_quantity',
|
||||
warehouse_location: 'inbound_buy:warehouse_location',
|
||||
unit_price: 'inbound_buy:unit_price',
|
||||
post_tax_unit_price: 'inbound_buy:post_tax_unit_price',
|
||||
tax_rate: 'inbound_buy:tax_rate',
|
||||
total_price: 'inbound_buy:total_price',
|
||||
currency: 'inbound_buy:currency',
|
||||
@ -889,7 +888,6 @@ const fieldOptions = computed(() => {
|
||||
{ value: 'qty_stock', label: '库存数', perm: 'inbound_buy:qty_stock' },
|
||||
{ value: 'qty_available', label: '可用数', perm: 'inbound_buy:qty_available' },
|
||||
{ value: 'unit_price', label: '不含税单价', perm: 'inbound_buy:unit_price' },
|
||||
{ value: 'post_tax_unit_price', label: '含税单价', perm: 'inbound_buy:post_tax_unit_price' },
|
||||
{ value: 'total_price', label: '不含税总价', perm: 'inbound_buy:total_price' },
|
||||
{ value: 'tax_rate', label: '税率', perm: 'inbound_buy:tax_rate' },
|
||||
{ value: 'currency', label: '币种', perm: 'inbound_buy:currency' },
|
||||
@ -941,7 +939,6 @@ const stockColumns = [
|
||||
|
||||
{prop: 'tax_rate', label: '税率', minWidth: '80'},
|
||||
{prop: 'unit_price', label: '不含税单价', minWidth: '120'},
|
||||
{prop: 'post_tax_unit_price', label: '含税单价', minWidth: '120'},
|
||||
|
||||
{prop: 'total_price', label: '不含税总价', minWidth: '120'},
|
||||
{prop: 'currency', label: '币种', minWidth: '80'},
|
||||
@ -979,7 +976,6 @@ const permissionMap: Record<string, string> = {
|
||||
warehouse_loc: 'inbound_buy:warehouse_loc',
|
||||
tax_rate: 'inbound_buy:tax_rate',
|
||||
unit_price: 'inbound_buy:unit_price',
|
||||
post_tax_unit_price: 'inbound_buy:post_tax_unit_price',
|
||||
total_price: 'inbound_buy:total_price',
|
||||
currency: 'inbound_buy:currency',
|
||||
exchange_rate: 'inbound_buy:exchange_rate',
|
||||
@ -1344,6 +1340,10 @@ const fetchData = async () => {
|
||||
isAsc: queryParams.isAsc,
|
||||
advancedFilters: JSON.stringify(queryParams.advancedFilters)
|
||||
}
|
||||
// 兼容旧版后端:company=ALL 时不传过滤参数
|
||||
if (params.company === 'ALL') {
|
||||
delete params.company
|
||||
}
|
||||
const res: any = await getBuyList(params)
|
||||
tableData.value = res.data.items || []
|
||||
total.value = res.data.total || 0
|
||||
@ -1684,7 +1684,7 @@ const resetAdvancedFilter = () => {
|
||||
fetchData()
|
||||
}
|
||||
const isColumnSortable = (prop: string) => {
|
||||
const sortableColumns = ['company_name', 'material_name', 'material_type', 'category', 'spec_model', 'unit', 'sku', 'barcode', 'inbound_date', 'serial_number', 'batch_number', 'status', 'inspection_status', 'qty_inbound', 'qty_stock', 'qty_available', 'warehouse_loc', 'unit_price', 'post_tax_unit_price', 'total_price', 'tax_rate', 'currency', 'exchange_rate', 'supplier_name', 'purchaser', 'purchaser_email', 'source_link', 'detail_link']
|
||||
const sortableColumns = ['company_name', 'material_name', 'material_type', 'category', 'spec_model', 'unit', 'sku', 'barcode', 'inbound_date', 'serial_number', 'batch_number', 'status', 'inspection_status', 'qty_inbound', 'qty_stock', 'qty_available', 'warehouse_loc', 'unit_price', 'total_price', 'tax_rate', 'currency', 'exchange_rate', 'supplier_name', 'purchaser', 'purchaser_email', 'source_link', 'detail_link']
|
||||
return sortableColumns.includes(prop)
|
||||
}
|
||||
const handleSortChange = ({ column, prop, order }: any) => {
|
||||
@ -1777,12 +1777,12 @@ const resetForm = () => {
|
||||
const getStatusType = (status: string) => { const map: any = {'在库': 'success', '出库': 'info', '损耗': 'danger'}; return map[status] || 'warning' }
|
||||
|
||||
// 列表金额显示增加千分位处理,并保留2位小数
|
||||
const formatMoney = (val: any, currency = '¥') => {
|
||||
const formatMoney = (val: any) => {
|
||||
const num = Number(val);
|
||||
if (isNaN(num)) return '-';
|
||||
const parts = num.toFixed(2).split('.');
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
return `${currency} ${parts.join('.')}`;
|
||||
return parts.join('.');
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
<div class="header-tools">
|
||||
<div class="left-tools">
|
||||
<el-select
|
||||
v-if="isSuperAdmin"
|
||||
v-model="queryParams.company"
|
||||
placeholder="所属公司"
|
||||
class="filter-item-select"
|
||||
@ -11,6 +12,7 @@
|
||||
@change="fetchData"
|
||||
style="width: 160px;"
|
||||
>
|
||||
<el-option label="全部 (跨域)" value="ALL" />
|
||||
<el-option v-for="item in companyOptions" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
|
||||
@ -620,6 +622,7 @@ const debounce = (fn: Function, delay: number = 500) => {
|
||||
|
||||
// ------------------------------------
|
||||
const userStore = useUserStore()
|
||||
const isSuperAdmin = computed(() => userStore.role === 'SUPER_ADMIN')
|
||||
const router = useRouter()
|
||||
|
||||
// 在新标签页打开基础信息编辑
|
||||
@ -647,7 +650,7 @@ const formRef = ref()
|
||||
// 上传锁定状态
|
||||
const isUploading = ref(false)
|
||||
|
||||
const queryParams = reactive({ page: 1, pageSize: 20, keyword: '', searchField: 'all', sku: '', category: '', material_type: '', statuses: ['在库', '借库'], company: '', orderByColumn: '', isAsc: '', advancedFilters: [] })
|
||||
const queryParams = reactive({ page: 1, pageSize: 20, keyword: '', searchField: 'all', sku: '', category: '', material_type: '', statuses: ['在库', '借库'], company: 'ALL', orderByColumn: '', isAsc: '', advancedFilters: [] })
|
||||
const categoryOptions = ref<string[]>([])
|
||||
const categoryTreeOptions = ref<{ value: string; label: string; children?: any[] }[]>([])
|
||||
|
||||
@ -1104,6 +1107,10 @@ const fetchData = async () => {
|
||||
isAsc: queryParams.isAsc,
|
||||
advancedFilters: queryParams.advancedFilters.length > 0 ? JSON.stringify(queryParams.advancedFilters) : ''
|
||||
}
|
||||
// 兼容旧版后端:company=ALL 时不传过滤参数
|
||||
if (params.company === 'ALL') {
|
||||
delete params.company
|
||||
}
|
||||
const res: any = await getProductList(params)
|
||||
tableData.value = res.data.items || []
|
||||
total.value = res.data.total || 0
|
||||
@ -1176,7 +1183,7 @@ const resetQuery = () => {
|
||||
queryParams.sku = ''
|
||||
queryParams.category = ''
|
||||
queryParams.material_type = ''
|
||||
queryParams.company = ''
|
||||
queryParams.company = isSuperAdmin.value ? 'ALL' : ''
|
||||
queryParams.page = 1
|
||||
queryParams.orderByColumn = ''
|
||||
queryParams.isAsc = ''
|
||||
@ -1458,7 +1465,7 @@ const resetForm = () => {
|
||||
}
|
||||
const getStatusType = (s:string) => ({'在库':'success','出库':'info','借库':'warning','损耗':'danger'}[s]||'warning')
|
||||
const getQualityType = (s:string) => ({'合格':'success','不合格':'danger','待检':'info'}[s]||'info')
|
||||
const formatMoney = (val:any) => isNaN(Number(val)) ? '-' : `¥ ${Number(val).toFixed(2)}`
|
||||
const formatMoney = (val:any) => isNaN(Number(val)) ? '-' : Number(val).toFixed(2)
|
||||
onMounted(() => {
|
||||
// 先根据权限初始化列显示状态
|
||||
initColumnPermissions()
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
<div class="left-tools">
|
||||
|
||||
<el-select
|
||||
v-if="isSuperAdmin"
|
||||
v-model="queryParams.company"
|
||||
placeholder="所属公司"
|
||||
class="filter-item-select"
|
||||
@ -12,6 +13,7 @@
|
||||
@change="fetchData"
|
||||
style="width: 160px;"
|
||||
>
|
||||
<el-option label="全部 (跨域)" value="ALL" />
|
||||
<el-option v-for="item in companyOptions" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
|
||||
@ -688,6 +690,7 @@ const vLoadmore = {
|
||||
// 状态与变量
|
||||
// ------------------------------------
|
||||
const userStore = useUserStore()
|
||||
const isSuperAdmin = computed(() => userStore.role === 'SUPER_ADMIN')
|
||||
const router = useRouter()
|
||||
|
||||
// 在新标签页打开基础信息编辑
|
||||
@ -716,7 +719,7 @@ const formRef = ref()
|
||||
// 上传锁定状态
|
||||
const isUploading = ref(false)
|
||||
|
||||
const queryParams = reactive({ page: 1, pageSize: 20, keyword: '', searchField: 'all', sku: '', category: '', material_type: '', statuses: ['在库', '借库'], company: '', orderByColumn: '', isAsc: '', advancedFilters: [] })
|
||||
const queryParams = reactive({ page: 1, pageSize: 20, keyword: '', searchField: 'all', sku: '', category: '', material_type: '', statuses: ['在库', '借库'], company: 'ALL', orderByColumn: '', isAsc: '', advancedFilters: [] })
|
||||
const categoryOptions = ref<string[]>([])
|
||||
const categoryTreeOptions = ref<{ value: string; label: string; children?: any[] }[]>([])
|
||||
|
||||
@ -1220,6 +1223,10 @@ const fetchData = async () => {
|
||||
isAsc: queryParams.isAsc,
|
||||
advancedFilters: queryParams.advancedFilters.length > 0 ? JSON.stringify(queryParams.advancedFilters) : ''
|
||||
}
|
||||
// 兼容旧版后端:company=ALL 时不传过滤参数
|
||||
if (params.company === 'ALL') {
|
||||
delete params.company
|
||||
}
|
||||
const res: any = await getSemiList(params)
|
||||
tableData.value = res.data.items || []
|
||||
total.value = res.data.total || 0
|
||||
@ -1292,7 +1299,7 @@ const resetQuery = () => {
|
||||
queryParams.sku = ''
|
||||
queryParams.category = ''
|
||||
queryParams.material_type = ''
|
||||
queryParams.company = ''
|
||||
queryParams.company = isSuperAdmin.value ? 'ALL' : ''
|
||||
queryParams.page = 1
|
||||
queryParams.orderByColumn = ''
|
||||
queryParams.isAsc = ''
|
||||
@ -1568,7 +1575,7 @@ const resetForm = () => {
|
||||
}
|
||||
const getStatusType = (status: string) => { const map: any = { '在库': 'success', '出库': 'info', '借库': 'warning', '损耗': 'danger' }; return map[status] || 'warning' }
|
||||
const getQualityType = (status: string) => { const map: any = { '合格': 'success', '不合格': 'danger', '待检': 'info', '返修中': 'warning' }; return map[status] || 'info' }
|
||||
const formatMoney = (val: any) => isNaN(Number(val)) ? '-' : `¥ ${Number(val).toFixed(2)}`
|
||||
const formatMoney = (val: any) => isNaN(Number(val)) ? '-' : Number(val).toFixed(2)
|
||||
|
||||
onMounted(() => {
|
||||
// 先根据权限初始化列显示状态
|
||||
|
||||
Reference in New Issue
Block a user