基础信息修改,新增所属公司,同时修正类别排序以及新增时候类别选择的功能
This commit is contained in:
@ -12,6 +12,9 @@ class MaterialBase(db.Model):
|
|||||||
|
|
||||||
# 1. 基础字段
|
# 1. 基础字段
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
# [修改] 所属公司,去除了 default='IRIS'
|
||||||
|
company_name = db.Column(db.String(255), comment='所属公司')
|
||||||
|
|
||||||
name = db.Column(db.String(255), nullable=False, comment='名称')
|
name = db.Column(db.String(255), nullable=False, comment='名称')
|
||||||
common_name = db.Column(db.String(255), comment='俗名')
|
common_name = db.Column(db.String(255), comment='俗名')
|
||||||
category = db.Column(db.String(100), comment='类别')
|
category = db.Column(db.String(100), comment='类别')
|
||||||
@ -42,8 +45,7 @@ class MaterialBase(db.Model):
|
|||||||
# 3. 关联成品库存 (StockProduct)
|
# 3. 关联成品库存 (StockProduct)
|
||||||
stock_products = db.relationship('StockProduct', back_populates='base', lazy='dynamic')
|
stock_products = db.relationship('StockProduct', back_populates='base', lazy='dynamic')
|
||||||
|
|
||||||
# 4. 关联服务库存 (StockService) - [新增]
|
# 4. 关联服务库存 (StockService)
|
||||||
# 假设您的服务库存模型类名为 StockService,且有 base_id 外键
|
|
||||||
stock_services = db.relationship('StockService', back_populates='base', lazy='dynamic')
|
stock_services = db.relationship('StockService', back_populates='base', lazy='dynamic')
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
@ -65,6 +67,7 @@ class MaterialBase(db.Model):
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
'id': self.id,
|
'id': self.id,
|
||||||
|
'companyName': self.company_name,
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
'commonName': self.common_name,
|
'commonName': self.common_name,
|
||||||
'category': self.category,
|
'category': self.category,
|
||||||
@ -72,7 +75,6 @@ class MaterialBase(db.Model):
|
|||||||
'spec': self.spec_model,
|
'spec': self.spec_model,
|
||||||
'unit': self.unit,
|
'unit': self.unit,
|
||||||
'visibilityLevel': self.visibility_level,
|
'visibilityLevel': self.visibility_level,
|
||||||
# 修改:解析为列表返回
|
|
||||||
'generalManual': parse_list(self.manual_link),
|
'generalManual': parse_list(self.manual_link),
|
||||||
'generalImage': parse_list(self.product_image),
|
'generalImage': parse_list(self.product_image),
|
||||||
'isEnabled': 1 if self.is_enabled else 0,
|
'isEnabled': 1 if self.is_enabled else 0,
|
||||||
|
|||||||
@ -4,7 +4,6 @@ from app.extensions import db
|
|||||||
from app.models.base import MaterialBase
|
from app.models.base import MaterialBase
|
||||||
from app.models.inbound.buy import StockBuy
|
from app.models.inbound.buy import StockBuy
|
||||||
from app.models.inbound.semi import StockSemi
|
from app.models.inbound.semi import StockSemi
|
||||||
# 假设您有 StockProduct 和 StockService 的模型定义
|
|
||||||
# from app.models.inbound.product import StockProduct
|
# from app.models.inbound.product import StockProduct
|
||||||
# from app.models.inbound.service import StockService
|
# from app.models.inbound.service import StockService
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
@ -33,7 +32,9 @@ class MaterialBaseService:
|
|||||||
or_(
|
or_(
|
||||||
MaterialBase.name.ilike(f'%{keyword}%'),
|
MaterialBase.name.ilike(f'%{keyword}%'),
|
||||||
MaterialBase.common_name.ilike(f'%{keyword}%'),
|
MaterialBase.common_name.ilike(f'%{keyword}%'),
|
||||||
MaterialBase.spec_model.ilike(f'%{keyword}%')
|
MaterialBase.spec_model.ilike(f'%{keyword}%'),
|
||||||
|
# 支持搜索公司名
|
||||||
|
MaterialBase.company_name.ilike(f'%{keyword}%')
|
||||||
)
|
)
|
||||||
).limit(20)
|
).limit(20)
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ class MaterialBaseService:
|
|||||||
for item in query.all():
|
for item in query.all():
|
||||||
results.append({
|
results.append({
|
||||||
'id': item.id,
|
'id': item.id,
|
||||||
|
'companyName': item.company_name,
|
||||||
'name': item.name,
|
'name': item.name,
|
||||||
'commonName': item.common_name,
|
'commonName': item.common_name,
|
||||||
'spec': item.spec_model,
|
'spec': item.spec_model,
|
||||||
@ -58,29 +60,21 @@ class MaterialBaseService:
|
|||||||
def _get_stock_counts(stock_query):
|
def _get_stock_counts(stock_query):
|
||||||
"""
|
"""
|
||||||
辅助函数:安全计算库存列表的总数量
|
辅助函数:安全计算库存列表的总数量
|
||||||
修复逻辑:优先查找 'stock_quantity' (Buy/Semi/Product表中实际使用的字段)
|
|
||||||
"""
|
"""
|
||||||
total_inv = 0
|
total_inv = 0
|
||||||
total_avail = 0
|
total_avail = 0
|
||||||
|
|
||||||
# 如果 stock_query 是动态加载的查询对象 (AppenderQuery),需要迭代它
|
|
||||||
# 如果是列表,直接迭代
|
|
||||||
try:
|
try:
|
||||||
items = list(stock_query) # 触发查询
|
items = list(stock_query) # 触发查询
|
||||||
except:
|
except:
|
||||||
items = []
|
items = []
|
||||||
|
|
||||||
for x in items:
|
for x in items:
|
||||||
# 1. 获取库存数
|
# 1. 获取库存数 (兼容不同字段名)
|
||||||
# 【修复点】根据你提供的 Service 代码,Buy/Semi/Product 均使用 stock_quantity
|
|
||||||
# Service 使用 actual_quantity,这里做兼容查找
|
|
||||||
q = getattr(x, 'stock_quantity', getattr(x, 'actual_quantity', getattr(x, 'quantity', 0)))
|
q = getattr(x, 'stock_quantity', getattr(x, 'actual_quantity', getattr(x, 'quantity', 0)))
|
||||||
|
|
||||||
# 2. 获取可用数
|
# 2. 获取可用数
|
||||||
# 这里的字段名通常都是 available_quantity
|
|
||||||
a = getattr(x, 'available_quantity', q)
|
a = getattr(x, 'available_quantity', q)
|
||||||
|
|
||||||
# 累加 (转 float 防止 None 或 Decimal 计算报错)
|
|
||||||
try:
|
try:
|
||||||
total_inv += float(q if q is not None else 0)
|
total_inv += float(q if q is not None else 0)
|
||||||
total_avail += float(a if a is not None else 0)
|
total_avail += float(a if a is not None else 0)
|
||||||
@ -93,13 +87,12 @@ class MaterialBaseService:
|
|||||||
def get_list(page, limit, filters=None):
|
def get_list(page, limit, filters=None):
|
||||||
"""
|
"""
|
||||||
获取基础信息列表 (带分页和筛选)
|
获取基础信息列表 (带分页和筛选)
|
||||||
并聚合库存总数和可用总数
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
query = MaterialBase.query
|
query = MaterialBase.query
|
||||||
|
|
||||||
if filters:
|
if filters:
|
||||||
# 1. 关键词模糊搜索 (名称 或 俗名 或 规格型号)
|
# 1. 关键词模糊搜索
|
||||||
if filters.get('keyword'):
|
if filters.get('keyword'):
|
||||||
kw = f"%{filters['keyword']}%"
|
kw = f"%{filters['keyword']}%"
|
||||||
query = query.filter(or_(
|
query = query.filter(or_(
|
||||||
@ -109,6 +102,10 @@ class MaterialBaseService:
|
|||||||
))
|
))
|
||||||
|
|
||||||
# 2. 精确筛选
|
# 2. 精确筛选
|
||||||
|
# 公司筛选
|
||||||
|
if filters.get('company'):
|
||||||
|
query = query.filter_by(company_name=filters['company'])
|
||||||
|
|
||||||
if filters.get('category'):
|
if filters.get('category'):
|
||||||
query = query.filter_by(category=filters['category'])
|
query = query.filter_by(category=filters['category'])
|
||||||
|
|
||||||
@ -116,7 +113,6 @@ class MaterialBaseService:
|
|||||||
query = query.filter_by(material_type=filters['type'])
|
query = query.filter_by(material_type=filters['type'])
|
||||||
|
|
||||||
if filters.get('isEnabled') is not None:
|
if filters.get('isEnabled') is not None:
|
||||||
# 前端传 1/0,转为 Boolean
|
|
||||||
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)
|
||||||
|
|
||||||
@ -125,25 +121,14 @@ class MaterialBaseService:
|
|||||||
|
|
||||||
items_list = []
|
items_list = []
|
||||||
for item in pagination.items:
|
for item in pagination.items:
|
||||||
# 获取基础字典
|
|
||||||
item_dict = item.to_dict()
|
item_dict = item.to_dict()
|
||||||
|
|
||||||
# [调用修复后的辅助函数]
|
# 聚合库存
|
||||||
|
|
||||||
# 1. 采购库存 (StockBuy)
|
|
||||||
buy_inv, buy_avail = MaterialBaseService._get_stock_counts(item.stock_buys)
|
buy_inv, buy_avail = MaterialBaseService._get_stock_counts(item.stock_buys)
|
||||||
|
|
||||||
# 2. 半成品库存 (StockSemi)
|
|
||||||
semi_inv, semi_avail = MaterialBaseService._get_stock_counts(item.stock_semis)
|
semi_inv, semi_avail = MaterialBaseService._get_stock_counts(item.stock_semis)
|
||||||
|
|
||||||
# 3. 成品库存 (StockProduct)
|
|
||||||
prod_inv, prod_avail = MaterialBaseService._get_stock_counts(item.stock_products)
|
prod_inv, prod_avail = MaterialBaseService._get_stock_counts(item.stock_products)
|
||||||
|
|
||||||
# 4. 服务库存 (StockService)
|
|
||||||
# 使用 getattr 防止关联不存在时报错
|
|
||||||
serv_inv, serv_avail = MaterialBaseService._get_stock_counts(getattr(item, 'stock_services', []))
|
serv_inv, serv_avail = MaterialBaseService._get_stock_counts(getattr(item, 'stock_services', []))
|
||||||
|
|
||||||
# 合并总数
|
|
||||||
item_dict['inventoryCount'] = buy_inv + semi_inv + prod_inv + serv_inv
|
item_dict['inventoryCount'] = buy_inv + semi_inv + prod_inv + serv_inv
|
||||||
item_dict['availableCount'] = buy_avail + semi_avail + prod_avail + serv_avail
|
item_dict['availableCount'] = buy_avail + semi_avail + prod_avail + serv_avail
|
||||||
|
|
||||||
@ -159,37 +144,45 @@ class MaterialBaseService:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def get_distinct_options():
|
def get_distinct_options():
|
||||||
"""
|
"""
|
||||||
获取所有已存在的类别和类型 (去重)
|
获取所有已存在的类别、类型、公司 (去重且排序)
|
||||||
用于前端下拉筛选
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# 查询所有不为空的类别并去重
|
# 1. 类别 (获取后在内存或前端做层级处理,这里先按字母序返回扁平列表)
|
||||||
categories = db.session.query(MaterialBase.category) \
|
categories = db.session.query(MaterialBase.category) \
|
||||||
.filter(MaterialBase.category != None, MaterialBase.category != '') \
|
.filter(MaterialBase.category != None, MaterialBase.category != '') \
|
||||||
.distinct().all()
|
.distinct().all()
|
||||||
|
|
||||||
# 查询所有不为空的类型并去重
|
# 对类别进行排序
|
||||||
|
sorted_categories = sorted([c[0] for c in categories])
|
||||||
|
|
||||||
|
# 2. 类型
|
||||||
types = db.session.query(MaterialBase.material_type) \
|
types = db.session.query(MaterialBase.material_type) \
|
||||||
.filter(MaterialBase.material_type != None, MaterialBase.material_type != '') \
|
.filter(MaterialBase.material_type != None, MaterialBase.material_type != '') \
|
||||||
.distinct().all()
|
.distinct().all()
|
||||||
|
sorted_types = sorted([t[0] for t in types])
|
||||||
|
|
||||||
|
# 3. 公司
|
||||||
|
companies = db.session.query(MaterialBase.company_name) \
|
||||||
|
.filter(MaterialBase.company_name != None, MaterialBase.company_name != '') \
|
||||||
|
.distinct().all()
|
||||||
|
sorted_companies = sorted([c[0] for c in companies])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"categories": [c[0] for c in categories],
|
"categories": sorted_categories,
|
||||||
"types": [t[0] for t in types]
|
"types": sorted_types,
|
||||||
|
"companies": sorted_companies
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return {"categories": [], "types": []}
|
return {"categories": [], "types": [], "companies": []}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_material(data):
|
def create_material(data):
|
||||||
"""新增基础信息"""
|
"""新增基础信息"""
|
||||||
try:
|
try:
|
||||||
# 0. 基础校验
|
|
||||||
if not data.get('name') or not data.get('spec'):
|
if not data.get('name') or not data.get('spec'):
|
||||||
raise ValueError("名称和规格型号不能为空")
|
raise ValueError("名称和规格型号不能为空")
|
||||||
|
|
||||||
# 1. 查重
|
|
||||||
exist = MaterialBase.query.filter_by(
|
exist = MaterialBase.query.filter_by(
|
||||||
name=data['name'],
|
name=data['name'],
|
||||||
spec_model=data['spec']
|
spec_model=data['spec']
|
||||||
@ -197,8 +190,9 @@ class MaterialBaseService:
|
|||||||
if exist:
|
if exist:
|
||||||
raise ValueError(f"已存在相同名称和规格的数据 (ID: {exist.id})")
|
raise ValueError(f"已存在相同名称和规格的数据 (ID: {exist.id})")
|
||||||
|
|
||||||
# 2. 创建对象 (列表转JSON字符串)
|
|
||||||
new_material = MaterialBase(
|
new_material = MaterialBase(
|
||||||
|
# [修改] 移除了 'IRIS' 默认值
|
||||||
|
company_name=data.get('companyName'),
|
||||||
name=data['name'],
|
name=data['name'],
|
||||||
common_name=data.get('commonName'),
|
common_name=data.get('commonName'),
|
||||||
spec_model=data['spec'],
|
spec_model=data['spec'],
|
||||||
@ -206,7 +200,6 @@ class MaterialBaseService:
|
|||||||
material_type=data.get('type'),
|
material_type=data.get('type'),
|
||||||
unit=data.get('unit'),
|
unit=data.get('unit'),
|
||||||
visibility_level=data.get('visibilityLevel'),
|
visibility_level=data.get('visibilityLevel'),
|
||||||
# 修改:将列表 dumps 为字符串
|
|
||||||
manual_link=json.dumps(data.get('generalManual', [])),
|
manual_link=json.dumps(data.get('generalManual', [])),
|
||||||
product_image=json.dumps(data.get('generalImage', [])),
|
product_image=json.dumps(data.get('generalImage', [])),
|
||||||
is_enabled=True if data.get('isEnabled', 1) == 1 else False
|
is_enabled=True if data.get('isEnabled', 1) == 1 else False
|
||||||
@ -229,6 +222,7 @@ class MaterialBaseService:
|
|||||||
raise ValueError("数据不存在")
|
raise ValueError("数据不存在")
|
||||||
|
|
||||||
# 更新字段
|
# 更新字段
|
||||||
|
if 'companyName' in data: material.company_name = data['companyName']
|
||||||
if 'name' in data: material.name = data['name']
|
if 'name' in data: material.name = data['name']
|
||||||
if 'commonName' in data: material.common_name = data['commonName']
|
if 'commonName' in data: material.common_name = data['commonName']
|
||||||
if 'spec' in data: material.spec_model = data['spec']
|
if 'spec' in data: material.spec_model = data['spec']
|
||||||
@ -237,7 +231,6 @@ class MaterialBaseService:
|
|||||||
if 'unit' in data: material.unit = data['unit']
|
if 'unit' in data: material.unit = data['unit']
|
||||||
if 'visibilityLevel' in data: material.visibility_level = data['visibilityLevel']
|
if 'visibilityLevel' in data: material.visibility_level = data['visibilityLevel']
|
||||||
|
|
||||||
# 修改:将列表 dumps 为字符串
|
|
||||||
if 'generalManual' in data:
|
if 'generalManual' in data:
|
||||||
material.manual_link = json.dumps(data['generalManual'])
|
material.manual_link = json.dumps(data['generalManual'])
|
||||||
if 'generalImage' in data:
|
if 'generalImage' in data:
|
||||||
@ -266,10 +259,6 @@ class MaterialBaseService:
|
|||||||
buy_usage_count = StockBuy.query.filter_by(base_id=m_id).count()
|
buy_usage_count = StockBuy.query.filter_by(base_id=m_id).count()
|
||||||
semi_usage_count = StockSemi.query.filter_by(base_id=m_id).count()
|
semi_usage_count = StockSemi.query.filter_by(base_id=m_id).count()
|
||||||
|
|
||||||
# 如果需要检查成品和服务,可以解开注释
|
|
||||||
# from app.models.inbound.product import StockProduct
|
|
||||||
# prod_usage_count = StockProduct.query.filter_by(base_id=m_id).count()
|
|
||||||
|
|
||||||
total_usage = buy_usage_count + semi_usage_count
|
total_usage = buy_usage_count + semi_usage_count
|
||||||
|
|
||||||
if total_usage > 0:
|
if total_usage > 0:
|
||||||
|
|||||||
@ -82,7 +82,7 @@ const handleLogout = () => {
|
|||||||
<footer v-if="!isLoginPage" class="app-footer">
|
<footer v-if="!isLoginPage" class="app-footer">
|
||||||
<span class="version-tag">
|
<span class="version-tag">
|
||||||
<el-icon style="vertical-align: middle; margin-right: 4px"><InfoFilled /></el-icon>
|
<el-icon style="vertical-align: middle; margin-right: 4px"><InfoFilled /></el-icon>
|
||||||
当前版本: 1.1 Beta (测试版)
|
当前版本: 1.2 Beta (2.24修改版)
|
||||||
</span>
|
</span>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -11,6 +11,18 @@
|
|||||||
@input="handleInputSearch"
|
@input="handleInputSearch"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.company"
|
||||||
|
placeholder="所属公司"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
default-first-option
|
||||||
|
style="width: 120px; margin-right: 10px;"
|
||||||
|
@change="handleQuery"
|
||||||
|
>
|
||||||
|
<el-option v-for="item in companyOptions" :key="item" :label="item" :value="item" />
|
||||||
|
</el-select>
|
||||||
|
|
||||||
<el-select
|
<el-select
|
||||||
v-model="queryParams.category"
|
v-model="queryParams.category"
|
||||||
placeholder="类别"
|
placeholder="类别"
|
||||||
@ -81,6 +93,7 @@
|
|||||||
列展示设置
|
列展示设置
|
||||||
</div>
|
</div>
|
||||||
<el-checkbox v-model="columns.id.visible" label="ID" />
|
<el-checkbox v-model="columns.id.visible" label="ID" />
|
||||||
|
<el-checkbox v-model="columns.companyName.visible" label="所属公司" />
|
||||||
<el-checkbox v-model="columns.name.visible" label="名称" />
|
<el-checkbox v-model="columns.name.visible" label="名称" />
|
||||||
<el-checkbox v-model="columns.commonName.visible" label="俗名" />
|
<el-checkbox v-model="columns.commonName.visible" label="俗名" />
|
||||||
<el-checkbox v-model="columns.category.visible" label="类别" />
|
<el-checkbox v-model="columns.category.visible" label="类别" />
|
||||||
@ -105,6 +118,13 @@
|
|||||||
style="width: 100%; margin-top: 15px"
|
style="width: 100%; margin-top: 15px"
|
||||||
>
|
>
|
||||||
<el-table-column v-if="columns.id.visible" prop="id" label="ID" min-width="80" align="center" fixed="left" />
|
<el-table-column v-if="columns.id.visible" prop="id" label="ID" min-width="80" align="center" fixed="left" />
|
||||||
|
|
||||||
|
<el-table-column v-if="columns.companyName.visible" prop="companyName" label="所属公司" min-width="100" align="center" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ scope.row.companyName || '-' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column v-if="columns.name.visible" prop="name" label="名称" min-width="160" show-overflow-tooltip />
|
<el-table-column v-if="columns.name.visible" prop="name" label="名称" min-width="160" show-overflow-tooltip />
|
||||||
|
|
||||||
<el-table-column v-if="columns.commonName.visible" prop="commonName" label="俗名" min-width="140" show-overflow-tooltip>
|
<el-table-column v-if="columns.commonName.visible" prop="commonName" label="俗名" min-width="140" show-overflow-tooltip>
|
||||||
@ -114,7 +134,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column v-if="columns.category.visible" prop="category" label="类别" min-width="120" align="center" show-overflow-tooltip>
|
<el-table-column v-if="columns.category.visible" prop="category" label="类别" min-width="140" show-overflow-tooltip>
|
||||||
<template #default="scope">{{ scope.row.category || '-' }}</template>
|
<template #default="scope">{{ scope.row.category || '-' }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column v-if="columns.type.visible" prop="type" label="类型" min-width="120" align="center" show-overflow-tooltip>
|
<el-table-column v-if="columns.type.visible" prop="type" label="类型" min-width="120" align="center" show-overflow-tooltip>
|
||||||
@ -196,19 +216,6 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<div style="margin-top: 20px; text-align: right;">
|
|
||||||
<el-pagination
|
|
||||||
background
|
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
|
||||||
:total="total"
|
|
||||||
v-model:current-page="queryParams.pageNum"
|
|
||||||
v-model:page-size="queryParams.pageSize"
|
|
||||||
:page-sizes="[100, 200, 500, 1000]"
|
|
||||||
@size-change="getList"
|
|
||||||
@current-change="getList"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="dialog.visible"
|
v-model="dialog.visible"
|
||||||
:title="dialog.title"
|
:title="dialog.title"
|
||||||
@ -233,16 +240,44 @@
|
|||||||
|
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="类别" prop="category">
|
<el-form-item label="所属公司" prop="companyName">
|
||||||
<el-autocomplete
|
<el-autocomplete
|
||||||
v-model="form.category"
|
v-model="form.companyName"
|
||||||
:fetch-suggestions="querySearchCategory"
|
:fetch-suggestions="querySearchCompany"
|
||||||
placeholder="可输入或选择"
|
placeholder="请输入公司名称"
|
||||||
clearable
|
clearable
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="类别" prop="category">
|
||||||
|
<div style="display: flex; width: 100%; align-items: center;">
|
||||||
|
<el-cascader
|
||||||
|
v-model="tempCategoryPrefix"
|
||||||
|
:options="categoryTreeOptions"
|
||||||
|
:props="{ expandTrigger: 'hover', checkStrictly: true, emitPath: true }"
|
||||||
|
placeholder="选择前缀层级"
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
style="width: 50%;"
|
||||||
|
/>
|
||||||
|
<div style="padding: 0 8px; font-weight: bold; color: #909399;">/</div>
|
||||||
|
<el-input
|
||||||
|
v-model="tempCategorySuffix"
|
||||||
|
placeholder="填写具体名称"
|
||||||
|
clearable
|
||||||
|
style="width: 50%;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 12px; color: #E6A23C; margin-top: 4px; line-height: 1.2;">
|
||||||
|
* 必须构成4层结构
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="类型" prop="type">
|
<el-form-item label="类型" prop="type">
|
||||||
<el-autocomplete
|
<el-autocomplete
|
||||||
@ -254,26 +289,27 @@
|
|||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="规格型号" prop="spec">
|
<el-form-item label="规格型号" prop="spec">
|
||||||
<el-input v-model="form.spec" placeholder="请输入规格型号" />
|
<el-input v-model="form.spec" placeholder="请输入规格型号" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="计量单位" prop="unit">
|
<el-form-item label="计量单位" prop="unit">
|
||||||
<el-input v-model="form.unit" placeholder="如: 个, 台, 米" />
|
<el-input v-model="form.unit" placeholder="如: 个, 台, 米" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="可见等级" prop="visibilityLevel">
|
||||||
|
<el-input-number v-model="form.visibilityLevel" :min="0" :max="9" label="等级" />
|
||||||
|
<span style="margin-left: 10px; color: #999; font-size: 12px;">(0低-9高)</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-form-item label="可见等级" prop="visibilityLevel">
|
|
||||||
<el-input-number v-model="form.visibilityLevel" :min="0" :max="9" label="等级" />
|
|
||||||
<span style="margin-left: 10px; color: #999; font-size: 12px;">(0为最低,9为最高)</span>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="产品图" prop="generalImage">
|
<el-form-item label="产品图" prop="generalImage">
|
||||||
<div class="upload-container">
|
<div class="upload-container">
|
||||||
<el-upload
|
<el-upload
|
||||||
@ -361,9 +397,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, onMounted, nextTick } from 'vue';
|
import { ref, reactive, onMounted, nextTick, computed } from 'vue';
|
||||||
import { Plus, Picture, Document, Refresh, Setting, Rank, Camera, Link } from '@element-plus/icons-vue';
|
import { Plus, Picture, Document, Refresh, Setting, Rank, Camera, Link } from '@element-plus/icons-vue';
|
||||||
// 修复:引入 ElLoading
|
|
||||||
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus';
|
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus';
|
||||||
import type { FormInstance, FormRules } from 'element-plus';
|
import type { FormInstance, FormRules } from 'element-plus';
|
||||||
|
|
||||||
@ -372,7 +407,7 @@ import {
|
|||||||
addMaterialBase,
|
addMaterialBase,
|
||||||
updateMaterialBase,
|
updateMaterialBase,
|
||||||
delMaterialBase,
|
delMaterialBase,
|
||||||
getMaterialBaseOptions // 新增引入
|
getMaterialBaseOptions
|
||||||
} from '@/api/material_base';
|
} from '@/api/material_base';
|
||||||
import { uploadFile, deleteFile } from '@/api/common/upload';
|
import { uploadFile, deleteFile } from '@/api/common/upload';
|
||||||
import WebRtcCamera from '@/components/Camera/WebRtcCamera.vue';
|
import WebRtcCamera from '@/components/Camera/WebRtcCamera.vue';
|
||||||
@ -380,6 +415,7 @@ import WebRtcCamera from '@/components/Camera/WebRtcCamera.vue';
|
|||||||
// --- 类型定义 ---
|
// --- 类型定义 ---
|
||||||
interface MaterialBaseVO {
|
interface MaterialBaseVO {
|
||||||
id: number;
|
id: number;
|
||||||
|
companyName: string;
|
||||||
name: string;
|
name: string;
|
||||||
commonName?: string;
|
commonName?: string;
|
||||||
category: string;
|
category: string;
|
||||||
@ -391,7 +427,6 @@ interface MaterialBaseVO {
|
|||||||
generalImage: string[];
|
generalImage: string[];
|
||||||
isEnabled: number;
|
isEnabled: number;
|
||||||
statusLoading?: boolean;
|
statusLoading?: boolean;
|
||||||
// 新增字段
|
|
||||||
inventoryCount?: number;
|
inventoryCount?: number;
|
||||||
availableCount?: number;
|
availableCount?: number;
|
||||||
}
|
}
|
||||||
@ -402,9 +437,16 @@ interface QueryParams {
|
|||||||
keyword: string;
|
keyword: string;
|
||||||
category: string;
|
category: string;
|
||||||
type: string;
|
type: string;
|
||||||
|
company: string;
|
||||||
isEnabled?: number;
|
isEnabled?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CascaderOption {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
children?: CascaderOption[];
|
||||||
|
}
|
||||||
|
|
||||||
// --- 响应式数据 ---
|
// --- 响应式数据 ---
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const total = ref(0);
|
const total = ref(0);
|
||||||
@ -425,28 +467,35 @@ const currentCameraField = ref<'generalImage' | 'generalManual'>('generalImage')
|
|||||||
|
|
||||||
const columns = reactive({
|
const columns = reactive({
|
||||||
id: { visible: true },
|
id: { visible: true },
|
||||||
|
companyName: { visible: true },
|
||||||
name: { visible: true },
|
name: { visible: true },
|
||||||
commonName: { visible: true },
|
commonName: { visible: true },
|
||||||
category: { visible: true },
|
category: { visible: true },
|
||||||
type: { visible: true },
|
type: { visible: true },
|
||||||
spec: { visible: true },
|
spec: { visible: true },
|
||||||
unit: { visible: true },
|
unit: { visible: true },
|
||||||
// visibilityLevel: { visible: false }, // 不再使用
|
inventory: { visible: true },
|
||||||
inventory: { visible: true }, // 新增
|
available: { visible: true },
|
||||||
available: { visible: true }, // 新增
|
|
||||||
files: { visible: true },
|
files: { visible: true },
|
||||||
isEnabled: { visible: true }
|
isEnabled: { visible: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const companyOptions = ref<string[]>([]);
|
||||||
const categoryOptions = ref<string[]>([]);
|
const categoryOptions = ref<string[]>([]);
|
||||||
const typeOptions = ref<string[]>([]);
|
const typeOptions = ref<string[]>([]);
|
||||||
|
const categoryTreeOptions = ref<CascaderOption[]>([]);
|
||||||
|
|
||||||
|
// [修改] 将类别拆分为前后两部分进行绑定
|
||||||
|
const tempCategoryPrefix = ref<string[]>([]); // 前缀部分 (Cascader)
|
||||||
|
const tempCategorySuffix = ref<string>(''); // 后缀部分 (Input)
|
||||||
|
|
||||||
const queryParams = reactive<QueryParams>({
|
const queryParams = reactive<QueryParams>({
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 100, // 修改默认显示条数为 100
|
pageSize: 100,
|
||||||
keyword: '',
|
keyword: '',
|
||||||
category: '',
|
category: '',
|
||||||
type: '',
|
type: '',
|
||||||
|
company: '',
|
||||||
isEnabled: undefined
|
isEnabled: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -460,6 +509,7 @@ const formRef = ref<FormInstance>();
|
|||||||
|
|
||||||
const initForm = {
|
const initForm = {
|
||||||
id: undefined,
|
id: undefined,
|
||||||
|
companyName: '',
|
||||||
name: '',
|
name: '',
|
||||||
commonName: '',
|
commonName: '',
|
||||||
category: '',
|
category: '',
|
||||||
@ -474,9 +524,40 @@ const initForm = {
|
|||||||
|
|
||||||
const form = ref({...initForm});
|
const form = ref({...initForm});
|
||||||
|
|
||||||
|
// [新增] 自定义验证规则:确保拼合后的类别符合4层结构
|
||||||
|
const validateCategoryLevel = (rule: any, value: any, callback: any) => {
|
||||||
|
// 实时计算拼合结果
|
||||||
|
const prefixStr = tempCategoryPrefix.value.join('/');
|
||||||
|
const suffixStr = tempCategorySuffix.value.trim();
|
||||||
|
|
||||||
|
// 如果两边都为空,报错
|
||||||
|
if (!prefixStr && !suffixStr) {
|
||||||
|
callback(new Error('请填写或选择类别'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拼合路径
|
||||||
|
let fullPath = '';
|
||||||
|
if (prefixStr && suffixStr) fullPath = prefixStr + '/' + suffixStr;
|
||||||
|
else if (prefixStr) fullPath = prefixStr;
|
||||||
|
else fullPath = suffixStr;
|
||||||
|
|
||||||
|
// 检查层级数量 (以 / 分割后的数组长度)
|
||||||
|
// 4层结构意味着有3个斜杠,例如 A/B/C/D => length 4
|
||||||
|
const levels = fullPath.split('/').filter(p => p.trim() !== '').length;
|
||||||
|
|
||||||
|
if (levels !== 4) {
|
||||||
|
callback(new Error(`必须严格满足4层结构,当前为 ${levels} 层`));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const rules = reactive<FormRules>({
|
const rules = reactive<FormRules>({
|
||||||
name: [{ required: true, message: '请输入基础信息名称', trigger: 'blur' }],
|
name: [{ required: true, message: '请输入基础信息名称', trigger: 'blur' }],
|
||||||
category: [{ required: true, message: '请选择或输入类别', trigger: 'change' }],
|
companyName: [{ required: true, message: '请输入公司名称', 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' }],
|
||||||
unit: [{ required: true, message: '请输入单位', trigger: 'blur' }]
|
unit: [{ required: true, message: '请输入单位', trigger: 'blur' }]
|
||||||
@ -484,22 +565,46 @@ const rules = reactive<FormRules>({
|
|||||||
|
|
||||||
// --- 业务逻辑方法 ---
|
// --- 业务逻辑方法 ---
|
||||||
|
|
||||||
// 获取所有选项(不再依赖当前页数据)
|
const buildCategoryTree = (categories: string[]): CascaderOption[] => {
|
||||||
|
const root: CascaderOption[] = [];
|
||||||
|
categories.forEach(cat => {
|
||||||
|
if (!cat) return;
|
||||||
|
const parts = cat.split('/');
|
||||||
|
let currentLevel = root;
|
||||||
|
parts.forEach((part, index) => {
|
||||||
|
let existingNode = currentLevel.find(n => n.value === part);
|
||||||
|
if (!existingNode) {
|
||||||
|
existingNode = { value: part, label: part };
|
||||||
|
currentLevel.push(existingNode);
|
||||||
|
}
|
||||||
|
if (index < parts.length - 1) {
|
||||||
|
if (!existingNode.children) {
|
||||||
|
existingNode.children = [];
|
||||||
|
}
|
||||||
|
currentLevel = existingNode.children;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return root;
|
||||||
|
};
|
||||||
|
|
||||||
const getOptionsList = () => {
|
const getOptionsList = () => {
|
||||||
getMaterialBaseOptions().then((res: any) => {
|
getMaterialBaseOptions().then((res: any) => {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
categoryOptions.value = res.data.categories || [];
|
categoryOptions.value = res.data.categories || [];
|
||||||
typeOptions.value = res.data.types || [];
|
typeOptions.value = res.data.types || [];
|
||||||
|
companyOptions.value = res.data.companies || [];
|
||||||
|
categoryTreeOptions.value = buildCategoryTree(categoryOptions.value);
|
||||||
}
|
}
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error("获取筛选项失败", err);
|
console.error("获取筛选项失败", err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const querySearchCategory = (queryString: string, cb: any) => {
|
const querySearchCompany = (queryString: string, cb: any) => {
|
||||||
const results = queryString
|
const results = queryString
|
||||||
? categoryOptions.value.filter(item => item.toLowerCase().includes(queryString.toLowerCase()))
|
? companyOptions.value.filter(item => item.toLowerCase().includes(queryString.toLowerCase()))
|
||||||
: categoryOptions.value;
|
: companyOptions.value;
|
||||||
const formattedResults = results.map(item => ({ value: item }));
|
const formattedResults = results.map(item => ({ value: item }));
|
||||||
cb(formattedResults);
|
cb(formattedResults);
|
||||||
};
|
};
|
||||||
@ -519,7 +624,6 @@ const getList = () => {
|
|||||||
if (response && response.data) {
|
if (response && response.data) {
|
||||||
tableData.value = response.data.items;
|
tableData.value = response.data.items;
|
||||||
total.value = response.data.total;
|
total.value = response.data.total;
|
||||||
// 移除 extractDynamicOptions 调用
|
|
||||||
} else {
|
} else {
|
||||||
tableData.value = [];
|
tableData.value = [];
|
||||||
total.value = 0;
|
total.value = 0;
|
||||||
@ -552,6 +656,7 @@ const resetQuery = () => {
|
|||||||
queryParams.keyword = '';
|
queryParams.keyword = '';
|
||||||
queryParams.category = '';
|
queryParams.category = '';
|
||||||
queryParams.type = '';
|
queryParams.type = '';
|
||||||
|
queryParams.company = '';
|
||||||
queryParams.isEnabled = undefined;
|
queryParams.isEnabled = undefined;
|
||||||
handleQuery();
|
handleQuery();
|
||||||
};
|
};
|
||||||
@ -572,17 +677,31 @@ const handleEdit = (row: MaterialBaseVO) => {
|
|||||||
dialog.visible = true;
|
dialog.visible = true;
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
// 基础字段赋值 - 深拷贝防止引用
|
const data = JSON.parse(JSON.stringify(row));
|
||||||
Object.assign(form.value, JSON.parse(JSON.stringify(row)));
|
Object.assign(form.value, data);
|
||||||
|
|
||||||
|
// [修改] 解析已有 category,拆分为 Prefix 和 Suffix
|
||||||
|
if (data.category) {
|
||||||
|
const parts = data.category.split('/');
|
||||||
|
if (parts.length > 0) {
|
||||||
|
// 取最后一部分作为 Suffix,其余作为 Prefix
|
||||||
|
tempCategorySuffix.value = parts.pop() || '';
|
||||||
|
tempCategoryPrefix.value = parts;
|
||||||
|
} else {
|
||||||
|
tempCategoryPrefix.value = [];
|
||||||
|
tempCategorySuffix.value = data.category;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tempCategoryPrefix.value = [];
|
||||||
|
tempCategorySuffix.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化文件列表
|
// 初始化文件列表
|
||||||
const images = row.generalImage || [];
|
const images = row.generalImage || [];
|
||||||
const manuals = row.generalManual || [];
|
const manuals = row.generalManual || [];
|
||||||
|
|
||||||
// 分离图片文件和外部链接
|
|
||||||
const imgFiles = images.filter(u => !isExternalLink(u));
|
const imgFiles = images.filter(u => !isExternalLink(u));
|
||||||
const imgLinks = images.filter(u => isExternalLink(u));
|
const imgLinks = images.filter(u => isExternalLink(u));
|
||||||
|
|
||||||
const manualFiles = manuals.filter(u => !isExternalLink(u));
|
const manualFiles = manuals.filter(u => !isExternalLink(u));
|
||||||
const manualLinks = manuals.filter(u => isExternalLink(u));
|
const manualLinks = manuals.filter(u => isExternalLink(u));
|
||||||
|
|
||||||
@ -619,23 +738,28 @@ const submitForm = async () => {
|
|||||||
if (valid) {
|
if (valid) {
|
||||||
submitLoading.value = true;
|
submitLoading.value = true;
|
||||||
try {
|
try {
|
||||||
// 重复校验
|
|
||||||
const isDuplicate = await checkDuplicate(form.value.name, form.value.spec);
|
const isDuplicate = await checkDuplicate(form.value.name, form.value.spec);
|
||||||
if (isDuplicate) {
|
if (isDuplicate) {
|
||||||
submitLoading.value = false;
|
submitLoading.value = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 整理文件数据:本地上传的路径已经在 form.value 中
|
|
||||||
// 我们需要重新合并:已有的非外链 + 新的输入框外链
|
|
||||||
const finalImageList = form.value.generalImage.filter(item => !isExternalLink(item));
|
const finalImageList = form.value.generalImage.filter(item => !isExternalLink(item));
|
||||||
if (imageExternalUrl.value) finalImageList.push(imageExternalUrl.value);
|
if (imageExternalUrl.value) finalImageList.push(imageExternalUrl.value);
|
||||||
|
|
||||||
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 suffixStr = tempCategorySuffix.value.trim();
|
||||||
|
let fullCategory = '';
|
||||||
|
if (prefixStr && suffixStr) fullCategory = prefixStr + '/' + suffixStr;
|
||||||
|
else fullCategory = prefixStr || suffixStr;
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
...form.value,
|
...form.value,
|
||||||
|
category: fullCategory,
|
||||||
generalImage: finalImageList,
|
generalImage: finalImageList,
|
||||||
generalManual: finalManualList
|
generalManual: finalManualList
|
||||||
};
|
};
|
||||||
@ -647,7 +771,6 @@ const submitForm = async () => {
|
|||||||
ElMessage.success(`${actionText}成功`);
|
ElMessage.success(`${actionText}成功`);
|
||||||
dialog.visible = false;
|
dialog.visible = false;
|
||||||
getList();
|
getList();
|
||||||
// 提交成功后,刷新选项,以防有新的类别/类型被创建
|
|
||||||
getOptionsList();
|
getOptionsList();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
ElMessage.error(error.msg || '保存失败');
|
ElMessage.error(error.msg || '保存失败');
|
||||||
@ -667,6 +790,11 @@ const resetForm = () => {
|
|||||||
form.value = JSON.parse(JSON.stringify(initForm));
|
form.value = JSON.parse(JSON.stringify(initForm));
|
||||||
fileListImage.value = [];
|
fileListImage.value = [];
|
||||||
fileListManual.value = [];
|
fileListManual.value = [];
|
||||||
|
|
||||||
|
// [修改] 重置前后缀
|
||||||
|
tempCategoryPrefix.value = [];
|
||||||
|
tempCategorySuffix.value = '';
|
||||||
|
|
||||||
imageExternalUrl.value = '';
|
imageExternalUrl.value = '';
|
||||||
manualExternalUrl.value = '';
|
manualExternalUrl.value = '';
|
||||||
if (formRef.value) formRef.value.resetFields();
|
if (formRef.value) formRef.value.resetFields();
|
||||||
@ -692,7 +820,6 @@ const handleDelete = (row: MaterialBaseVO) => {
|
|||||||
ElMessage.success("删除成功");
|
ElMessage.success("删除成功");
|
||||||
if (tableData.value.length === 1 && queryParams.pageNum > 1) queryParams.pageNum--;
|
if (tableData.value.length === 1 && queryParams.pageNum > 1) queryParams.pageNum--;
|
||||||
getList();
|
getList();
|
||||||
// 删除后也刷新选项
|
|
||||||
getOptionsList();
|
getOptionsList();
|
||||||
});
|
});
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
@ -762,7 +889,6 @@ const triggerCamera = (field: 'generalImage' | 'generalManual') => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleCameraConfirm = async (file: File) => {
|
const handleCameraConfirm = async (file: File) => {
|
||||||
console.log('✅ 父组件收到照片:', file.name)
|
|
||||||
if (!beforeAvatarUpload(file)) {
|
if (!beforeAvatarUpload(file)) {
|
||||||
cameraDialogVisible.value = false;
|
cameraDialogVisible.value = false;
|
||||||
return;
|
return;
|
||||||
@ -770,7 +896,6 @@ const handleCameraConfirm = async (file: File) => {
|
|||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
|
|
||||||
// 修复点:使用 ElLoading
|
|
||||||
const loadingInstance = ElLoading.service({ text: '照片上传中...', background: 'rgba(0, 0, 0, 0.7)' });
|
const loadingInstance = ElLoading.service({ text: '照片上传中...', background: 'rgba(0, 0, 0, 0.7)' });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -801,7 +926,7 @@ const handleCameraConfirm = async (file: File) => {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getList();
|
getList();
|
||||||
getOptionsList(); // 初始化下拉选项
|
getOptionsList();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user