对于页面展示内容进行按照规格型号进行排序,同时修改类别下拉框大小
This commit is contained in:
@ -36,12 +36,32 @@ class MaterialBaseService:
|
|||||||
# 支持搜索公司名
|
# 支持搜索公司名
|
||||||
MaterialBase.company_name.ilike(f'%{keyword}%')
|
MaterialBase.company_name.ilike(f'%{keyword}%')
|
||||||
)
|
)
|
||||||
).limit(20)
|
)
|
||||||
|
|
||||||
|
# [修改1] 增加返回数量限制
|
||||||
|
# 原为 limit(20),现改为 1000,确保前端能获取所有(或足够多)的数据
|
||||||
|
query = query.limit(1000)
|
||||||
|
|
||||||
|
# 获取查询结果对象列表
|
||||||
|
db_items = query.all()
|
||||||
|
|
||||||
|
# [修改2] 规格型号排序逻辑
|
||||||
|
# 要求:只考虑 '/' 前面的内容进行排序
|
||||||
|
# 使用 Python 的 sort 方法,提取 spec_model 中 '/' 前的部分
|
||||||
|
def get_sort_key(item):
|
||||||
|
if not item.spec_model:
|
||||||
|
return ""
|
||||||
|
# 如果包含 '/',取前半部分;否则取整个字符串
|
||||||
|
parts = item.spec_model.split('/')
|
||||||
|
return parts[0] if len(parts) > 0 else item.spec_model
|
||||||
|
|
||||||
|
# 执行排序
|
||||||
|
db_items.sort(key=get_sort_key)
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
for item in query.all():
|
for item in db_items:
|
||||||
results.append({
|
results.append({
|
||||||
'id': item.id,
|
'id': item.id, # 必须保留ID供前端逻辑使用,视觉上的隐藏请在前端处理
|
||||||
'companyName': item.company_name,
|
'companyName': item.company_name,
|
||||||
'name': item.name,
|
'name': item.name,
|
||||||
'commonName': item.common_name,
|
'commonName': item.common_name,
|
||||||
@ -116,8 +136,10 @@ class MaterialBaseService:
|
|||||||
is_active = bool(int(filters['isEnabled']))
|
is_active = bool(int(filters['isEnabled']))
|
||||||
query = query.filter_by(is_enabled=is_active)
|
query = query.filter_by(is_enabled=is_active)
|
||||||
|
|
||||||
# 按 ID 倒序排列
|
# [修改3] 默认排序方式改为按 spec_model 排序
|
||||||
pagination = query.order_by(MaterialBase.id.desc()).paginate(page=page, per_page=limit, error_out=False)
|
# 如果需要更复杂的“/前内容”排序,通常直接按字符串排序也能满足前缀分组的需求
|
||||||
|
pagination = query.order_by(MaterialBase.spec_model.asc()).paginate(page=page, per_page=limit,
|
||||||
|
error_out=False)
|
||||||
|
|
||||||
items_list = []
|
items_list = []
|
||||||
for item in pagination.items:
|
for item in pagination.items:
|
||||||
|
|||||||
@ -30,8 +30,9 @@
|
|||||||
filterable
|
filterable
|
||||||
allow-create
|
allow-create
|
||||||
default-first-option
|
default-first-option
|
||||||
style="width: 140px; margin-right: 10px;"
|
style="width: 240px; margin-right: 10px;"
|
||||||
@change="handleQuery"
|
@change="handleQuery"
|
||||||
|
popper-class="long-dropdown"
|
||||||
>
|
>
|
||||||
<el-option v-for="item in categoryOptions" :key="item" :label="item" :value="item" />
|
<el-option v-for="item in categoryOptions" :key="item" :label="item" :value="item" />
|
||||||
</el-select>
|
</el-select>
|
||||||
@ -45,6 +46,7 @@
|
|||||||
default-first-option
|
default-first-option
|
||||||
style="width: 140px; margin-right: 10px;"
|
style="width: 140px; margin-right: 10px;"
|
||||||
@change="handleQuery"
|
@change="handleQuery"
|
||||||
|
popper-class="long-dropdown"
|
||||||
>
|
>
|
||||||
<el-option v-for="item in typeOptions" :key="item" :label="item" :value="item" />
|
<el-option v-for="item in typeOptions" :key="item" :label="item" :value="item" />
|
||||||
</el-select>
|
</el-select>
|
||||||
@ -479,7 +481,8 @@ const cameraRef = ref<InstanceType<typeof WebRtcCamera> | null>(null);
|
|||||||
const currentCameraField = ref<'generalImage' | 'generalManual'>('generalImage');
|
const currentCameraField = ref<'generalImage' | 'generalManual'>('generalImage');
|
||||||
|
|
||||||
const columns = reactive({
|
const columns = reactive({
|
||||||
id: { visible: true },
|
// [修改] 默认隐藏 ID
|
||||||
|
id: { visible: false },
|
||||||
companyName: { visible: true },
|
companyName: { visible: true },
|
||||||
name: { visible: true },
|
name: { visible: true },
|
||||||
commonName: { visible: true },
|
commonName: { visible: true },
|
||||||
@ -498,9 +501,8 @@ const categoryOptions = ref<string[]>([]);
|
|||||||
const typeOptions = ref<string[]>([]);
|
const typeOptions = ref<string[]>([]);
|
||||||
const categoryTreeOptions = ref<CascaderOption[]>([]);
|
const categoryTreeOptions = ref<CascaderOption[]>([]);
|
||||||
|
|
||||||
// [修改] 将类别拆分为前后两部分进行绑定
|
const tempCategoryPrefix = ref<string[]>([]);
|
||||||
const tempCategoryPrefix = ref<string[]>([]); // 前缀部分 (Cascader)
|
const tempCategorySuffix = ref<string>('');
|
||||||
const tempCategorySuffix = ref<string>(''); // 后缀部分 (Input)
|
|
||||||
|
|
||||||
const queryParams = reactive<QueryParams>({
|
const queryParams = reactive<QueryParams>({
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
@ -537,26 +539,20 @@ const initForm = {
|
|||||||
|
|
||||||
const form = ref({...initForm});
|
const form = ref({...initForm});
|
||||||
|
|
||||||
// [新增] 自定义验证规则:确保拼合后的类别符合4层结构
|
|
||||||
const validateCategoryLevel = (rule: any, value: any, callback: any) => {
|
const validateCategoryLevel = (rule: any, value: any, callback: any) => {
|
||||||
// 实时计算拼合结果
|
|
||||||
const prefixStr = tempCategoryPrefix.value.join('/');
|
const prefixStr = tempCategoryPrefix.value.join('/');
|
||||||
const suffixStr = tempCategorySuffix.value.trim();
|
const suffixStr = tempCategorySuffix.value.trim();
|
||||||
|
|
||||||
// 如果两边都为空,报错
|
|
||||||
if (!prefixStr && !suffixStr) {
|
if (!prefixStr && !suffixStr) {
|
||||||
callback(new Error('请填写或选择类别'));
|
callback(new Error('请填写或选择类别'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拼合路径
|
|
||||||
let fullPath = '';
|
let fullPath = '';
|
||||||
if (prefixStr && suffixStr) fullPath = prefixStr + '/' + suffixStr;
|
if (prefixStr && suffixStr) fullPath = prefixStr + '/' + suffixStr;
|
||||||
else if (prefixStr) fullPath = prefixStr;
|
else if (prefixStr) fullPath = prefixStr;
|
||||||
else fullPath = suffixStr;
|
else fullPath = suffixStr;
|
||||||
|
|
||||||
// 检查层级数量 (以 / 分割后的数组长度)
|
|
||||||
// 4层结构意味着有3个斜杠,例如 A/B/C/D => length 4
|
|
||||||
const levels = fullPath.split('/').filter(p => p.trim() !== '').length;
|
const levels = fullPath.split('/').filter(p => p.trim() !== '').length;
|
||||||
|
|
||||||
if (levels !== 4) {
|
if (levels !== 4) {
|
||||||
@ -569,7 +565,6 @@ const validateCategoryLevel = (rule: any, value: any, callback: any) => {
|
|||||||
const rules = reactive<FormRules>({
|
const rules = reactive<FormRules>({
|
||||||
name: [{ required: true, message: '请输入基础信息名称', trigger: 'blur' }],
|
name: [{ required: true, message: '请输入基础信息名称', trigger: 'blur' }],
|
||||||
companyName: [{ required: true, message: '请输入公司名称', trigger: 'change' }],
|
companyName: [{ required: true, message: '请输入公司名称', trigger: 'change' }],
|
||||||
// [修改] 使用自定义验证器
|
|
||||||
category: [{ required: true, validator: validateCategoryLevel, trigger: 'change' }],
|
category: [{ required: true, validator: validateCategoryLevel, trigger: 'change' }],
|
||||||
type: [{ required: true, message: '请选择或输入类型', trigger: 'change' }],
|
type: [{ required: true, message: '请选择或输入类型', trigger: 'change' }],
|
||||||
spec: [{ required: true, message: '请输入规格型号', trigger: 'blur' }],
|
spec: [{ required: true, message: '请输入规格型号', trigger: 'blur' }],
|
||||||
@ -678,14 +673,12 @@ const handleSizeChange = (command: 'large' | 'default' | 'small') => {
|
|||||||
tableSize.value = command;
|
tableSize.value = command;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 新增:分页大小改变处理
|
|
||||||
const handlePageSizeChange = (val: number) => {
|
const handlePageSizeChange = (val: number) => {
|
||||||
queryParams.pageSize = val;
|
queryParams.pageSize = val;
|
||||||
queryParams.pageNum = 1; // 切换大小时重置到第一页
|
queryParams.pageNum = 1;
|
||||||
getList();
|
getList();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 新增:当前页码改变处理
|
|
||||||
const handlePageCurrentChange = (val: number) => {
|
const handlePageCurrentChange = (val: number) => {
|
||||||
queryParams.pageNum = val;
|
queryParams.pageNum = val;
|
||||||
getList();
|
getList();
|
||||||
@ -706,11 +699,9 @@ const handleEdit = (row: MaterialBaseVO) => {
|
|||||||
const data = JSON.parse(JSON.stringify(row));
|
const data = JSON.parse(JSON.stringify(row));
|
||||||
Object.assign(form.value, data);
|
Object.assign(form.value, data);
|
||||||
|
|
||||||
// [修改] 解析已有 category,拆分为 Prefix 和 Suffix
|
|
||||||
if (data.category) {
|
if (data.category) {
|
||||||
const parts = data.category.split('/');
|
const parts = data.category.split('/');
|
||||||
if (parts.length > 0) {
|
if (parts.length > 0) {
|
||||||
// 取最后一部分作为 Suffix,其余作为 Prefix
|
|
||||||
tempCategorySuffix.value = parts.pop() || '';
|
tempCategorySuffix.value = parts.pop() || '';
|
||||||
tempCategoryPrefix.value = parts;
|
tempCategoryPrefix.value = parts;
|
||||||
} else {
|
} else {
|
||||||
@ -722,7 +713,6 @@ const handleEdit = (row: MaterialBaseVO) => {
|
|||||||
tempCategorySuffix.value = '';
|
tempCategorySuffix.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化文件列表
|
|
||||||
const images = row.generalImage || [];
|
const images = row.generalImage || [];
|
||||||
const manuals = row.generalManual || [];
|
const manuals = row.generalManual || [];
|
||||||
|
|
||||||
@ -776,7 +766,6 @@ const submitForm = async () => {
|
|||||||
const finalManualList = form.value.generalManual.filter(item => !isExternalLink(item));
|
const finalManualList = form.value.generalManual.filter(item => !isExternalLink(item));
|
||||||
if (manualExternalUrl.value) finalManualList.push(manualExternalUrl.value);
|
if (manualExternalUrl.value) finalManualList.push(manualExternalUrl.value);
|
||||||
|
|
||||||
// [修改] 提交前组合字符串:Prefix + / + Suffix
|
|
||||||
const prefixStr = tempCategoryPrefix.value.join('/');
|
const prefixStr = tempCategoryPrefix.value.join('/');
|
||||||
const suffixStr = tempCategorySuffix.value.trim();
|
const suffixStr = tempCategorySuffix.value.trim();
|
||||||
let fullCategory = '';
|
let fullCategory = '';
|
||||||
@ -817,7 +806,6 @@ const resetForm = () => {
|
|||||||
fileListImage.value = [];
|
fileListImage.value = [];
|
||||||
fileListManual.value = [];
|
fileListManual.value = [];
|
||||||
|
|
||||||
// [修改] 重置前后缀
|
|
||||||
tempCategoryPrefix.value = [];
|
tempCategoryPrefix.value = [];
|
||||||
tempCategorySuffix.value = '';
|
tempCategorySuffix.value = '';
|
||||||
|
|
||||||
@ -982,14 +970,12 @@ onMounted(() => {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 新增:分页样式 */
|
|
||||||
.pagination-container {
|
.pagination-container {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 上传相关样式 */
|
|
||||||
.upload-container { display: flex; flex-wrap: wrap; gap: 8px; }
|
.upload-container { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||||
:deep(.el-upload--picture-card) { width: 100px; height: 100px; line-height: 100px; }
|
:deep(.el-upload--picture-card) { width: 100px; height: 100px; line-height: 100px; }
|
||||||
:deep(.el-upload-list--picture-card .el-upload-list__item) { width: 100px; height: 100px; }
|
:deep(.el-upload-list--picture-card .el-upload-list__item) { width: 100px; height: 100px; }
|
||||||
@ -998,7 +984,14 @@ onMounted(() => {
|
|||||||
.camera-card .text { font-size: 12px; margin-top: 5px; }
|
.camera-card .text { font-size: 12px; margin-top: 5px; }
|
||||||
.camera-card .el-icon { font-size: 24px; }
|
.camera-card .el-icon { font-size: 24px; }
|
||||||
|
|
||||||
/* 表格缩略图样式 */
|
|
||||||
.file-preview-cell { display: flex; align-items: center; justify-content: center; position: relative; }
|
.file-preview-cell { display: flex; align-items: center; justify-content: center; position: relative; }
|
||||||
.more-badge { position: absolute; top: -5px; right: -5px; background: #909399; color: #fff; border-radius: 10px; padding: 0 4px; font-size: 10px; transform: scale(0.9); }
|
.more-badge { position: absolute; top: -5px; right: -5px; background: #909399; color: #fff; border-radius: 10px; padding: 0 4px; font-size: 10px; transform: scale(0.9); }
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* 增加下拉框的最大高度,使其能容纳更多选项而不必频繁滚动 */
|
||||||
|
.long-dropdown .el-select-dropdown__wrap {
|
||||||
|
max-height: 600px !important; /* 可以根据屏幕大小适当调整 */
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user