Merge pull request 'main' (#8) from main into prod

Reviewed-on: #8
This commit is contained in:
charles 2025-08-19 16:06:07 +08:00
commit f18655e3f7
46 changed files with 2394 additions and 603 deletions

View File

@ -90,7 +90,7 @@ async def email_info(msg, indent=0):
index = find_data.index("<")
name = find_data[:index]
if price and name:
mail_code_sql = """SELECT * FROM mail_code WHERE LOCATE(mailcode, name) > 0 and del_flg = '0';"""
mail_code_sql = """SELECT * FROM mail_code WHERE LOCATE(mailcode, '%s') > 0 and del_flg = '0';""" % name
mail_code = await sor.sqlExe(mail_code_sql, {})
# mail_code = await sor.R('mail_code',{'mailcode':name,'del_flg':'0'})
date = await get_business_date(sor=None)

View File

@ -720,9 +720,11 @@ async def baidu_confirm_auto_renew_order(ns={}):
# 读取baidu_orders表格 确定状态是renew的订单ID
renew_results = []
return {'1': 2}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
renew_sql = """"SELECT bo.*, bs.user_id FROM baidu_orders AS bo LEFT JOIN baidu_users AS bs ON bo.accountid = bs.baidu_id WHERE bo.ordertype = 'RENEW' AND bo.status = 'NEED_CONFIRM' AND bo.del_flg = '0';"""
return {'1': renew_sql}
renew_results = await sor.sqlExe(renew_sql, {})
renew_status_count = 0
@ -746,7 +748,6 @@ async def baidu_confirm_auto_renew_order(ns={}):
'renew_status_count': renew_status_count,
'update_msg': update_msg,
}
}
ret = await baidu_confirm_auto_renew_order(params_kw)

View File

@ -875,53 +875,6 @@ detailDataLJS = {
}
],
"products": [
{
"title": "NVIDIA-4090",
"description": "配备GPU的云端服务器可以为机器学习、高性能计算、图形图像渲染等计算密集型应用提供加速处理能力。根据需求场景的不同既可提供弹性的GPU云服务器也可提供高性能的GPU裸金属服务器助力您的业务创新提升竞争优势。",
"price": "6000",
"pre_price": "7143",
"price_unit": "台/月",
"discount": 8.4,
"bgImgUrl": "https://www.kaiyuancloud.cn/idfile?path=firstpagebg/1.png",
"list": [
{
"id": 1,
"name": "CPU",
"content": "AMD EPYC 7542 32 C * 2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssc2x.png"
},
{
"id": 2,
"name": "内存",
"content": "64G DDR4-3200 * 8",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/cpu2x.png"
},
{
"id": 3,
"name": "GPU",
"content": "NVIDIA-4090-24GB * 8",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/sdcard2x.png"
},
{
"id": 4,
"name": "系统盘",
"content": "960G SATA SSD * 2 (Raid)",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/servermini2x.png"
},
{
"id": 5,
"name": "数据盘",
"content": "3.84T U.2 NVMe SSD * 1",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/serverpath2x.png"
},
{
"id": 6,
"name": "网卡",
"content": "Mellanox Connect4 25G SFP28 2-port * 1",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssdr2x.png"
}
]
},
{
"title": "NVIDIA-4090",
"description": "轻量应用服务器Light server是一种内置应用型镜像或系统型镜像的小规格云服务器 绑定适配大小的磁盘和带宽为中小型企业和个人用户提供官网搭建、web应用搭建、云上学习和测试等场景的服务。",

View File

@ -0,0 +1,16 @@
async def homepage_category_tree_delete(ns={}):
if not ns.get('id'):
return {
'status': False,
'msg': '请传递分类ID'
}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
await sor.U('homepage_category_tree', {'id': ns.get('id'), 'del_flg': '1'})
return {
'status': True,
'msg': 'product category deleted successfully'
}
ret = await homepage_category_tree_delete(params_kw)
return ret

View File

@ -0,0 +1,36 @@
async def homepage_product_category(ns={}):
if not ns.get('publish_type'):
return {
'status': False,
'msg': '请传递产品类型'
}
publish_type = ns.get('publish_type')
domain_name = ns.get('url_link').split("//")[1].split("/")[0]
if 'localhost' in domain_name:
domain_name = 'dev.opencomputing.cn'
db = DBPools()
async with db.sqlorContext('kboss') as sor:
# 读取user_publish_product产品
user_publish_product_sql = """SELECT DISTINCT(product_category) FROM user_publish_product WHERE domain_name = '%s' AND first_page = '1' AND audit_status = 'approved' AND listing_status = 'listing' AND del_flg = '0' AND publish_type = '%s' ORDER BY create_at DESC;""" % (domain_name, publish_type)
user_publish_product_result = await sor.sqlExe(user_publish_product_sql, {})
if not user_publish_product_result:
return {
'status': False,
'msg': '没有找到匹配的产品'
}
user_publish_product_ids = [item['product_category'].split(",")[0] for item in user_publish_product_result]
# 读取user_publish_product_category产品类别
user_publish_product_category_sql = """SELECT * FROM user_publish_product_category WHERE domain_name = '%s' AND del_flg = '0' ORDER BY priority ASC;""" % domain_name
user_publish_product_category_result = await sor.sqlExe(user_publish_product_category_sql, {})
result = [item for item in user_publish_product_category_result if str(item['id']) in user_publish_product_ids]
return {
'status': True,
'msg': 'success',
'data': result
}
ret = await homepage_product_category(params_kw)
return ret

View File

@ -91,6 +91,7 @@ async def publish_product_add(ns={}):
if user_role != '客户':
ns['status'] = '1'
ns_dic['audit_status'] = 'approved'
ns_dic['listing_status'] = 'listing'
ns_dic['first_page'] = '1'
try:
await sor.C('user_publish_product', ns_dic)

View File

@ -1,3 +1,121 @@
async def publish_product_to_excel(ns={}):
if not ns.get('ids') and (not ns.get('result_from_search')):
return {
'status': False,
'msg': '请传递产品ID'
}
result = []
if ns.get('ids'):
db = DBPools()
async with db.sqlorContext('kboss') as sor:
ids = json.loads(ns.get('ids')) if isinstance(ns.get('ids'), str) else ns.get('ids')
# 根据ids查询user_publish_product表
find_sql = """SELECT * FROM user_publish_product WHERE id IN (%s) AND del_flg = '0';""" % ','.join(["'%s'" % id for id in ids])
result = await sor.sqlExe(find_sql, {})
if not result:
return {
'status': False,
'msg': '没有找到匹配的产品'
}
if ns.get('result_from_search'):
result = ns.get('result_from_search')
# 结果转换成 中文名称:值 的字典列表
field_mapping = {
# 'id': 'id',
# 'publish_type': '类型',
'img': '图片链接',
# 'cart_flag': '是否支持显卡',
# 'domain_name': '所属域名',
# 'orgid': '所属机构',
'product_name': '商品名称',
# 'product_category': '所属类别',
'company_name': '企业名称',
# 'company_type': '公司类别',
'contact_person': '联系人',
'job_title': '职务',
'phone_number': '手机号码',
'email': '邮箱',
'cpu': 'cpu',
'memory': '内存',
'gpu': 'gpu',
'sys_disk': '系统盘',
'data_disk': '数据盘',
'net_card': '网卡',
'price': '价格',
'unit': '价格单位',
'discount': '价格折扣',
'discount_price': '折扣后价格',
'service_charge': '服务费',
'short_term': '是否短租',
# 'priority': '排序优先级',
'status': '上架状态',
# 'title': '主题',
# 'label': '标签',
# 'first_page': '是否推送到首页',
# 'admin_push': '是否是运营人员提交',
'requirement_summary': '需求概述',
'related_parameters': '相关参数',
'application_scenario': '应用场景',
'audit_status': '审核状态',
'listing_status': '上架状态',
'reject_reason': '驳回原因',
'update_time': '更新时间',
'create_at': '创建时间',
# 'publish_time': '发布日期',
# 'del_flg': '删除标志'
}
# 转换字典键为中文
for data_dic in result:
# 如果有图片路径 则转换为完整的图片链接
if data_dic.get('img') and data_dic['img'] != 'null':
data_dic['img'] = 'https://' + data_dic['domain_name'] + '/idfile?path=' + data_dic['img']
else:
data_dic['img'] = None
# 转换字典键为中文 对应的值要有映射
# 拆分后:显式循环结构(便于后续处理)
new_data_dic = {}
for key, value in data_dic.items():
if key == 'publish_type':
# 显示类型映射
if value == '1':
value = '产品'
elif value == '2':
value = '需求'
if key == 'audit_status':
# 显示审核状态映射
if value == 'pending':
value = '待审'
elif value == 'approved':
value = '通过'
elif value == 'rejected':
value = '拒绝'
if key == 'listing_status':
# 显示上架状态映射
if value == 'listing':
value = '上架'
elif value == 'delisting':
value = '下架'
if key == 'short_term':
# 显示短租状态映射
if value == '1':
value = '是'
elif value == '0':
value = '否'
# 获取中文映射键
chinese_key = field_mapping.get(key)
# 仅保留有映射关系的字段
if chinese_key is not None:
new_data_dic[chinese_key] = value
data_dic.clear()
data_dic.update(new_data_dic)
return {
'status': True,
'msg': 'export to excel success',
'data': result
}
async def get_user_role(ns={}):
sor = ns['sor']
# get role
@ -36,6 +154,8 @@ async def publish_product_search(ns={}):
"""
offset = ns.get('offset')
manager_self = ns.get('manager_self')
to_excel = ns.get('to_excel')
if ns.get('userid'):
userid = ns.get('userid')
@ -46,6 +166,12 @@ async def publish_product_search(ns={}):
'status': False,
'msg': 'url_link is required'
}
if not ns.get('publish_type'):
return {
'status': False,
'msg': 'publish_type is required'
}
domain_name = ns.get('url_link').split("//")[1].split("/")[0]
if 'localhost' in domain_name:
@ -141,6 +267,23 @@ async def publish_product_search(ns={}):
res['img'] = 'https://' + domain_name + '/idfile?path=' + res['img']
else:
res['img'] = None
# 读取产品类别表 轮询匹配result中的所有类别
product_category_list = await sor.R('user_publish_product_category', {'del_flg': '0'})
for res in result:
for product_category in product_category_list:
if product_category['id'] == res['product_category'].split(",")[0]:
res['category_name'] = product_category['product_category']
break
if to_excel == '1':
res = await publish_product_to_excel({'result_from_search': result})
return {
'status': True,
'msg': 'search to excel success',
'data': res['data']
}
else:
return {
'status': True,
'msg': 'Requirements retrieved successfully',

View File

@ -23,6 +23,57 @@ async def get_user_role(ns={}):
return role
async def user_browse_history_add(ns={}):
# ns = {
# 'userid': '9KVhsVCJsW_29q3hRhMAr', # 用户ID
# 'product_id': 'p2s2YPPU7uquza3gGw9k2', # 产品ID
# 'ip_address': '192.168.1.1', # IP地址
# 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/98.0.4758.102' # 用户代理
# }
# 必要参数校验
if not ns.get('userid') or not ns.get('product_id'):
return {
'status': False,
'msg': 'userid and product_id are required'
}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
# 根据timestamp时间字段:browse_time去重 查找10个小时内是否存在
browse_time = datetime.datetime.now() - datetime.timedelta(hours=10)
check_sql = """
SELECT * FROM user_browse_history
WHERE userid = '%s' AND product_id = '%s' AND del_flg = '0' AND browse_time >= '%s';
""" % (ns.get('userid'), ns.get('product_id'), browse_time)
check_result = await sor.sqlExe(check_sql, {})
if check_result:
return {
'status': False,
'msg': 'The user has browsed this product within the last 10 hours'
}
# 生成记录ID
record_id = uuid()
# 插入浏览记录
insert_sql = """
INSERT INTO user_browse_history (
id, userid, product_id, ip_address, user_agent, del_flg
) VALUES ('%s', '%s', '%s', '%s', '%s', '0');
""" % (record_id, ns.get('userid'), ns.get('product_id'),
ns.get('ip_address', ''), ns.get('user_agent', ''))
await sor.sqlExe(insert_sql, {})
return {
'status': True,
'msg': 'Browse history recorded successfully',
'data': {'record_id': record_id}
}
except Exception as e:
return {
'status': False,
'msg': 'Failed to record browse history, %s' % str(e)
}
async def publish_product_search_detail(ns={}):
"""
:param ns:
@ -36,6 +87,7 @@ async def publish_product_search_detail(ns={}):
db = DBPools()
async with db.sqlorContext('kboss') as sor:
user_role = None
orgid = None
# 区分运营和普通客户
if userid:
user_list = await sor.R('users', {'id': userid})
@ -44,8 +96,12 @@ async def publish_product_search_detail(ns={}):
# 如果用户角色是运营并且from='b' product_list中的phone_number和email做加星号处理
product_list = await sor.R('user_publish_product', {'id': ns.get('id'), 'del_flg': '0'})
product_category = await sor.R('user_publish_product_category', {'id': product_list[0]['product_category'].split(',')[0]})
product_list[0]['product_category'] = product_category[0]['product_category']
if user_role == '运营' and ns.get('from') == 'b':
pass
elif orgid == product_list[0]['orgid']:
pass
else:
for product in product_list:
if product.get('phone_number'):
@ -57,6 +113,10 @@ async def publish_product_search_detail(ns={}):
else:
product['email'] = 'kawa@****.com'
# 保存浏览记录
if userid:
await user_browse_history_add({'userid': userid, 'product_id': ns.get('id')})
return {
'status': True,
'msg': 'Product retrieved successfully',

View File

@ -20,8 +20,12 @@ async def publish_product_search_first_page(ns={}):
async with db.sqlorContext('kboss') as sor:
try:
ns['del_flg'] = '0'
if to_page == 'first_page':
count_sql = """SELECT COUNT(*) AS total_count FROM user_publish_product WHERE domain_name = '%s' AND first_page = '1' AND audit_status = 'approved' AND del_flg = '0' AND publish_type = '%s' ORDER BY create_at DESC;""" % (domain_name, publish_type)
# 如果是首页并且有产品分类
if to_page == 'first_page' and product_category:
count_sql = """SELECT COUNT(*) AS total_count FROM user_publish_product WHERE domain_name = '%s' AND first_page = '1' AND audit_status = 'approved' AND listing_status = 'listing' AND del_flg = '0' AND publish_type = '%s' AND product_category LIKE '%%%%%s%%%%';""" % (domain_name, publish_type, product_category)
find_sql = """SELECT upp.* FROM user_publish_product AS upp WHERE upp.domain_name = '%s' AND upp.first_page = '1' AND upp.audit_status = 'approved' AND listing_status = 'listing' AND upp.product_category LIKE '%%%%%s%%%%' AND upp.del_flg = '0' AND upp.publish_type = '%s' ORDER BY upp.priority ASC, upp.create_at DESC LIMIT %s OFFSET %s;""" % (domain_name, product_category, publish_type, page_size, current_page)
elif to_page == 'first_page':
count_sql = """SELECT COUNT(*) AS total_count FROM user_publish_product WHERE domain_name = '%s' AND first_page = '1' AND audit_status = 'approved' AND listing_status = 'listing' AND del_flg = '0' AND publish_type = '%s' ORDER BY create_at DESC;""" % (domain_name, publish_type)
find_sql = """SELECT upp.* FROM user_publish_product AS upp INNER JOIN user_publish_product_category AS uppc ON upp.product_category LIKE uppc.id WHERE upp.domain_name = '%s' AND upp.first_page = '1' AND upp.audit_status = 'approved' AND upp.del_flg = '0' AND upp.publish_type = '%s' ORDER BY uppc.priority ASC, upp.create_at DESC LIMIT %s OFFSET %s;""" % (domain_name, publish_type, page_size, current_page)
elif to_page == 'square' and product_category:
count_sql = """SELECT COUNT(*) AS total_count FROM user_publish_product WHERE domain_name = '%s' AND product_category LIKE """ % domain_name + """'%%""" + product_category + """%%'""" + """ AND audit_status = 'approved' AND del_flg = '0' AND publish_type = '%s' ORDER BY create_at DESC;""" % publish_type

View File

@ -68,10 +68,11 @@ async def publish_product_update(ns={}):
# 非客户角色编辑客户的产品
if user_role != '客户' and ns.get('orgid') != orgid:
# 非客户角色审核客户的产品
# 如果是待审状态 更新审核状态为通过 上架状态为上架
# 如果是待审状态 更新审核状态为通过 上架状态为上架 清空驳回原因
if ns.get('audit_status') == 'approved':
ns_dic['audit_status'] = 'approved'
ns_dic['listing_status'] = 'listing'
ns_dic['reject_reason'] = None
# 如果是上架商品 更新publish_time为当前时间
if ns.get('listing_status') == 'listing':
ns_dic['publish_time'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

View File

@ -12,6 +12,42 @@ async def search_user_inquiry(ns={}):
async with db.sqlorContext('kboss') as sor:
search_sql = """select * from product_inquiry where domain_name = '%s' and del_flg = '0' order by update_time desc;""" % domain_name
result = await sor.sqlExe(search_sql, {})
if ns.get('to_excel') == '1':
# 创建映射字段 导出execl
# 结果转换成 中文名称:值 的字典列表
field_mapping = {
'name': '联系人姓名',
'custom_type': '客户类型',
'phone': '联系人电话',
'email': '邮箱',
'company': '公司名称',
'content': '咨询内容',
'feedback': '反馈状态',
}
# 新增值映射字典,集中管理各字段的数值转换规则
value_mapping = {
'custom_type': {'0': '个人', '1': '企业'},
'feedback': {'0': '未反馈', '1': '已反馈'} # 根据表结构补充反馈状态映射
}
# 转换字典键为中文
for data_dic in result:
# 拆分后:显式循环结构(便于后续处理)
new_data_dic = {}
# 按field_mapping定义的顺序处理字段确保输出顺序一致
for key in field_mapping.keys():
# 跳过数据中不存在的字段
if key not in data_dic:
continue
value = data_dic[key]
chinese_key = field_mapping[key]
if key in value_mapping:
mapped_value = value_mapping[key].get(str(value), value) # 若未找到对应映射,保留原始值
new_data_dic[chinese_key] = mapped_value
else:
new_data_dic[chinese_key] = value
data_dic.clear()
data_dic.update(new_data_dic)
return {
'status': True,
'msg': 'search success',

View File

@ -29,6 +29,14 @@ async def enterprise_audit_info_search(ns={}):
'status': False,
'msg': '请传递url_link'
}
# 分页
current_page = int(ns['current_page']) if ns.get('current_page') else 1
page_size = int(ns['page_size']) if ns.get('page_size') else 10
offset = (current_page - 1) * page_size
audit_status = ns.get('audit_status')
domain_name = ns.get('url_link').split("//")[1].split("/")[0]
if 'localhost' in domain_name:
domain_name = 'dev.opencomputing.cn'
@ -50,10 +58,36 @@ async def enterprise_audit_info_search(ns={}):
user_role = await get_user_role({'userid': userid, 'sor': sor})
try:
if user_role == '客户': # 客户查询
count_sql = """SELECT COUNT(*) AS total_count FROM enterprise_audit_info WHERE orgid = '%s' AND del_flg = '0';""" % orgid
find_sql = """SELECT * FROM enterprise_audit_info WHERE orgid = '%s' AND del_flg = '0';""" % orgid
else: # 运营查询 enterprise_audit_info和organization表关联查询 enterprise_audit_info中的orgid和organization表中的id关联查询
find_sql = """SELECT eai.* FROM enterprise_audit_info AS eai LEFT JOIN organization as org ON eai.orgid = org.id WHERE org.parentid = '%s' AND eai.del_flg = '0' ORDER BY eai.update_time DESC;""" % orgid
count_sql = """SELECT COUNT(*) AS total_count FROM enterprise_audit_info AS eai LEFT JOIN organization as org ON eai.orgid = org.id WHERE org.parentid = '%s' AND eai.del_flg = '0';""" % orgid
find_sql = """SELECT eai.* FROM enterprise_audit_info AS eai LEFT JOIN organization as org ON eai.orgid = org.id WHERE org.parentid = '%s' AND eai.del_flg = '0' ORDER BY eai.update_time DESC LIMIT %s OFFSET %s;""" % (orgid, page_size, offset)
# 拆分find_sql 增加audit_status条件筛选
if audit_status:
# Split the audit_status string into individual statuses
statuses = [status.strip() for status in audit_status.split(',') if status.strip()]
if statuses:
# Create a properly escaped list of statuses for SQL
escaped_statuses = []
for status in statuses:
# Escape single quotes by doubling them (SQL standard)
escaped_status = status.replace("'", "''")
escaped_statuses.append(f"'{escaped_status}'")
# Join the escaped statuses for the IN clause
statuses_str = ','.join(escaped_statuses)
if statuses_str:
count_sql = count_sql.split("WHERE")[0] + f"WHERE eai.audit_status IN ({statuses_str}) AND " + count_sql.split("WHERE")[1]
find_sql = find_sql.split("WHERE")[0] + f"WHERE eai.audit_status IN ({statuses_str}) AND " + find_sql.split("WHERE")[1]
# 非pending
else:
count_sql = count_sql.split("WHERE")[0] + "WHERE eai.audit_status != 'pending' AND " + count_sql.split("WHERE")[1]
find_sql = find_sql.split("WHERE")[0] + "WHERE eai.audit_status != 'pending' AND " + find_sql.split("WHERE")[1]
# 执行查询
total_count = (await sor.sqlExe(count_sql, {}))[0]['total_count']
res = await sor.sqlExe(find_sql, {})
# 处理结果中的图片路径 增加前缀
@ -66,8 +100,13 @@ async def enterprise_audit_info_search(ns={}):
return {
'status': True,
'msg': 'enterprise audit info search successfully',
'data': {
'total_count': total_count,
'current_page': current_page,
'page_size': page_size,
'data': res
}
}
except Exception as e:
return {
'status': False,

View File

@ -0,0 +1,53 @@
async def user_browse_history_add(ns={}):
# ns = {
# 'userid': '9KVhsVCJsW_29q3hRhMAr', # 用户ID
# 'product_id': 'p2s2YPPU7uquza3gGw9k2', # 产品ID
# 'ip_address': '192.168.1.1', # IP地址
# 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/98.0.4758.102' # 用户代理
# }
# 必要参数校验
if not ns.get('userid') or not ns.get('product_id'):
return {
'status': False,
'msg': 'userid and product_id are required'
}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
# 根据timestamp时间字段:browse_time去重 查找10个小时内是否存在
browse_time = datetime.datetime.now() - datetime.timedelta(hours=10)
check_sql = """
SELECT * FROM user_browse_history
WHERE userid = '%s' AND product_id = '%s' AND del_flg = '0' AND browse_time >= '%s';
""" % (ns.get('userid'), ns.get('product_id'), browse_time)
check_result = await sor.sqlExe(check_sql, {})
if check_result:
return {
'status': False,
'msg': 'The user has browsed this product within the last 10 hours'
}
# 生成记录ID
record_id = uuid()
# 插入浏览记录
insert_sql = """
INSERT INTO user_browse_history (
id, userid, product_id, ip_address, user_agent, del_flg
) VALUES ('%s', '%s', '%s', '%s', '%s', '0');
""" % (record_id, ns.get('userid'), ns.get('product_id'),
ns.get('ip_address', ''), ns.get('user_agent', ''))
await sor.sqlExe(insert_sql, {})
return {
'status': True,
'msg': 'Browse history recorded successfully',
'data': {'record_id': record_id}
}
except Exception as e:
return {
'status': False,
'msg': 'Failed to record browse history, %s' % str(e)
}
ret = await user_browse_history_add(params_kw)
return ret

View File

@ -0,0 +1,29 @@
async def user_browse_history_delete(ns={}):
if not ns.get('id'):
return {
'status': False,
'msg': 'record id is required'
}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
# 逻辑删除(更新删除标志)
update_sql = """
UPDATE user_browse_history
SET del_flg = '1'
WHERE id = '%s';
""" % ns.get('id')
await sor.sqlExe(update_sql, {})
return {
'status': True,
'msg': 'Browse history deleted successfully'
}
except Exception as e:
return {
'status': False,
'msg': 'Failed to delete browse history, %s' % str(e)
}
ret = await user_browse_history_delete(params_kw)
return ret

View File

@ -0,0 +1,72 @@
async def user_browse_history_search(ns={}):
# 处理userid
if ns.get('userid'):
userid = ns.get('userid')
else:
userid = await get_user()
if not userid:
return {
'status': False,
'msg': 'no match user'
}
# 参数处理
product_id = ns.get('product_id')
start_time = ns.get('start_time')
end_time = ns.get('end_time')
page_size = int(ns.get('page_size', 10))
current_page = int(ns.get('current_page', 1))
offset = (current_page - 1) * page_size
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
# 基础查询条件
base_conditions = ["del_flg = '0'"]
if userid:
base_conditions.append(f"userid = '{userid}'")
if product_id:
base_conditions.append(f"product_id = '{product_id}'")
if start_time and end_time:
end_time += ' 23:59:59'
base_conditions.append(f"browse_time BETWEEN '{start_time}' AND '{end_time}'")
# 构建查询SQL
where_clause = " AND ".join(base_conditions)
# 根据product_id查询product_info
find_sql = f"""
SELECT * FROM user_browse_history
WHERE {where_clause}
ORDER BY browse_time DESC
LIMIT {page_size} OFFSET {offset};
"""
# 总数查询SQL
count_sql = f"""
SELECT COUNT(*) AS total_count FROM user_browse_history
WHERE {where_clause};
"""
# 执行查询
result = await sor.sqlExe(find_sql, {})
total_count = (await sor.sqlExe(count_sql, {}))[0]['total_count']
return {
'status': True,
'msg': 'Browse history retrieved successfully',
'data': {
'total_count': total_count,
'page_size': page_size,
'current_page': current_page,
'history_list': result
}
}
except Exception as e:
return {
'status': False,
'msg': 'Failed to retrieve browse history, %s' % str(e)
}
ret = await user_browse_history_search(params_kw)
return ret

View File

View File

View File

@ -125,3 +125,61 @@ export function reqExportProduct(data){
data
})
}
//产品添加子级和兄弟节点
export function reqAddProductMenu(data){
return request({
url: '/product/homepage_category_tree_add.dspy',
method: 'post',
headers: { 'Content-Type': 'application/json' },
data
})
}
//资质查询
export function reqEnterpriseAuditInfoSearch(data){
return request({
url: '/user/enterprise_audit_info_search.dspy',
method: 'post',
headers: { 'Content-Type': 'application/json' },
data
})
}
//咨询表单 /product/search_user_inquiry.dspy
export function reqSearchUserInquiry(data){
return request({
url: '/product/search_user_inquiry.dspy',
method: 'post',
headers: { 'Content-Type': 'application/json' },
data
})
}
//审核状态查询 /user/enterprise_audit_info_search.dspy
export function reqApproveUserSearch(data){
return request({
url: '/user/enterprise_audit_info_search.dspy',
method: 'post',
headers: { 'Content-Type': 'application/json' },
data
})
}
//政企审核 更新 /user/enterprise_audit_info_update.dspy
export function reqEnterpriseUpdate(data){
return request({
url: '/user/enterprise_audit_info_update.dspy',
method: 'post',
headers: { 'Content-Type': 'application/json' },
data
})
}
//首页类别查询 /product/homepage_product_category.dspy
export function reqHomepageProductCategory(data){
return request({
url: '/product/homepage_product_category.dspy',
method: 'post',
headers: { 'Content-Type': 'application/json' },
data
})
}

View File

@ -24,8 +24,9 @@
<!-- </div>-->
<div @click="open2"
style="position:absolute;bottom:-40px;display: flex;justify-content: center;align-items: center;cursor: pointer">
<el-tooltip class="item" effect="light" content="联系销售" placement="left">
<el-tooltip v-if="getHomePath()!=='/ncmatchHome/index'" class="item" effect="light" content="联系销售" placement="left">
<span class="el-icon-video-camera"
style="font-weight: 550;font-size: 22px;"></span>
</el-tooltip>
@ -213,6 +214,7 @@ import {contantAPI, applyAPI} from "../../api/floatingWindow/index";
import axios from "axios";
import {mapState} from "vuex";
import {getCustomerOfSaleUserId} from "@/api/customer/vedio";
import { getHomePath } from "@/views/setting/tools";
export default {
//
@ -285,6 +287,8 @@ export default {
},
computed: {},
methods: {
// getHomePath()
getHomePath,
openTip() {
this.$notify({
title: '提示',

View File

@ -516,6 +516,7 @@ import PromotionalInvitationCode from "@/views/customer/promotionalInvitationCod
import {reqApplyChannel} from "@/api/customer/channel";
import {reqSearchProductList} from "@/api/product/product";
import {bdProducts, jdProducts} from "@/views/product/productionData";
import { getHomePath } from "@/views/setting/tools";
export default {
data() {
@ -1238,7 +1239,7 @@ export default {
sessionStorage.removeItem('userId')
this.$store.commit('setLoginState', false)
await this.$router.push(`/homePage/index`);
await this.$router.push(getHomePath());
// window.location.reload()
},

View File

@ -301,89 +301,7 @@ export const asyncRoutes = [
// name: 'productHome',
// meta: {title: "概览", fullPath: "/productHome", noCache: true}
// },
{
hidden: true,
path: "/approveMangement",
component:Layout,
name: "approveMangement",
redirect: "/approveMangement/index",
meta: { fullPath: "/approveMangement", title: "审核管理", noCache: true, icon: 'el-icon-s-home' },
children: [
{
path: "pendingPro",
component: () => import("@/views/customer/approveMangement/pendingPro.vue"),
name: "pendingPro",
meta: { title: "待审清单", fullPath: "/approveMangement/index" },
},
{
path: "approvedPro",
component: () => import("@/views/customer/approveMangement/approvedPro.vue"),
name: "approvedPro",
meta: { title: "已审清单", fullPath: "/approveMangement/approvedPro" },
},
]
},
{
hidden: true,
path: "/productMangement",
component:Layout,
name: "productMangement",
redirect: "/productMangement/index",
meta: { fullPath: "/productMangement", title: "商品管理", noCache: true, icon: 'el-icon-s-home' },
children: [
{
path: "index",
component: () => import("@/views/customer/productMangement/productList/index.vue"),
name: "productList",
meta: { title: "商品发布清单", fullPath: "/productMangement/index" },
},
{
path: "rejectedPro",
component: () => import("@/views/customer/productMangement/rejectedPro/index.vue"),
name: "rejectedPro",
meta: { title: "商品未通过清单", fullPath: "/productMangement/index" },
},
]
},
{
hidden: true,
path: "/menuMangement",
component:Layout,
name: "menuMangement",
redirect: "/menuMangement/index",
meta: { fullPath: "/menuMangement", title: "菜单管理", noCache: true, icon: 'el-icon-s-home' },
children: [
{
path: "index",
component: () => import("@/views/operation/menuMangement/index.vue"),
name: "menuMangement",
meta: { title: "菜单管理", fullPath: "/menuMangement/index" },
},
]
},
{
hidden: true,
path: "/demandMangement",
component:Layout,
name: "demandMangement",
redirect: "/demandMangement/index",
meta: { fullPath: "/demandMangement", title: "需求管理", noCache: true, icon: 'el-icon-s-home' },
children: [
{
path: "index",
component: () => import("@/views/customer/demand/demandList.vue"),
name: "demandList",
meta: { title: "需求发布清单", fullPath: "/demandMangement/index" },
},
{
path: "rejectDemand",
component: () => import("@/views/customer/demand/rejectDemand.vue"),
name: "rejectDemand",
meta: { title: "需求未通过清单", fullPath: "/demandMangement/rejectDemand" },
},
]
},
{
path: getHomePath() == '/ncmatchHome/index' ? "/ncmatchHome" : "/homePage",
@ -427,6 +345,20 @@ export const asyncRoutes = [
}]
},
{
path: "/consultingMangement",
name: 'ConsultingMangement',
component: Layout,
meta: { title: "咨询表单", fullPath: "/consultingMangement", noCache: true,icon: "el-icon-s-platform" },
children: [
{
path: "index",
component: () => import('@/views/operation/consultingMangement/index.vue'),
name: 'ConsultingMangement',
meta: { title: "咨询表单", fullPath: "/consultingMangement/index", noCache: true,icon: "el-icon-s-platform" },
}
]
},
{
path: "/product",
name: 'product',
@ -821,7 +753,7 @@ export const asyncRoutes = [
path: 'approve',
component: () => import('@/views/customer/ncApprove/index.vue'),
name: "Approve",
meta: { title: "信息审核", fullPath: "/customer/ncApprove" },
meta: { title: "信息完善", fullPath: "/customer/ncApprove" },
}, {
hidden: true,
path: "channelMangement",
@ -936,7 +868,110 @@ export const asyncRoutes = [
name: "ProductIndex",
meta: { title: "产品最新页", fullPath: "/product/productHome/productIndex", noCache: true },
},],
}, {
},
{
path: "/qualificationReview",
component:Layout,
name: "qualificationReview",
redirect: "/qualificationReview/index",
meta: { fullPath: "/qualificationReview", title: "资质审核", noCache: true, icon: 'el-icon-s-home' },
children: [
{
path: "noApproveInfo",
component: () => import("@/views/customer/qualificationReview/noApproveInfo/index.vue"),
name: "noApproveInfo",
meta: { title: "待审清单", fullPath: "/qualificationReview/index" },
},
{
path: "rejectInfo",
component: () => import("@/views/customer/qualificationReview/apprvedInfo/index.vue"),
name: "rejectInfo",
meta: { title: "已审清单", fullPath: "/qualificationReview/approvedInfo" },
},
]
},
{
path: "/approveMangement",
component:Layout,
name: "approveMangement",
redirect: "/approveMangement/index",
meta: { fullPath: "/approveMangement", title: "供需审核", noCache: true, icon: 'el-icon-s-home' },
children: [
{
path: "pendingPro",
component: () => import("@/views/customer/approveMangement/pendingPro.vue"),
name: "pendingPro",
meta: { title: "待审清单", fullPath: "/approveMangement/index" },
},
{
path: "approvedPro",
component: () => import("@/views/customer/approveMangement/approvedPro.vue"),
name: "approvedPro",
meta: { title: "已审清单", fullPath: "/approveMangement/approvedPro" },
},
]
},
{
path: "/productMangement",
component:Layout,
name: "productMangement",
redirect: "/productMangement/index",
meta: { fullPath: "/productMangement", title: "商品管理", noCache: true, icon: 'el-icon-s-home' },
children: [
{
path: "index",
component: () => import("@/views/customer/productMangement/productList/index.vue"),
name: "productList",
meta: { title: "商品发布清单", fullPath: "/productMangement/index" },
},
{
path: "rejectedPro",
component: () => import("@/views/customer/productMangement/rejectedPro/index.vue"),
name: "rejectedPro",
meta: { title: "商品未通过清单", fullPath: "/productMangement/index" },
},
]
},
{
path: "/menuMangement",
component:Layout,
name: "menuMangement",
redirect: "/menuMangement/index",
meta: { fullPath: "/menuMangement", title: "菜单管理", noCache: true, icon: 'el-icon-s-home' },
children: [
{
path: "index",
component: () => import("@/views/operation/menuMangement/index.vue"),
name: "menuMangement",
meta: { title: "菜单管理", fullPath: "/menuMangement/index" },
},
]
},
{
path: "/demandMangement",
component:Layout,
name: "demandMangement",
redirect: "/demandMangement/index",
meta: { fullPath: "/demandMangement", title: "需求管理", noCache: true, icon: 'el-icon-s-home' },
children: [
{
path: "index",
component: () => import("@/views/customer/demand/demandList.vue"),
name: "demandList",
meta: { title: "需求发布清单", fullPath: "/demandMangement/index" },
},
{
path: "rejectDemand",
component: () => import("@/views/customer/demand/rejectDemand.vue"),
name: "rejectDemand",
meta: { title: "需求未通过清单", fullPath: "/demandMangement/rejectDemand" },
},
]
},
{
path: "/sales", component: Layout,
meta: {

View File

@ -14,7 +14,8 @@ export default {
return {
role:{
role_type:'customer',
audit_status:'pending'
audit_status:'pending',
type:'app'
}
}
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1755568430743" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1525" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14"><path d="M782.848 728.576m-185.856 0a185.856 185.856 0 1 0 371.712 0 185.856 185.856 0 1 0-371.712 0Z" fill="#FFE200" p-id="1526"></path><path d="M194.56 166.4H128c-16.896 0-30.72-13.824-30.72-30.72s13.824-30.72 30.72-30.72h66.56c16.896 0 30.72 13.824 30.72 30.72s-13.824 30.72-30.72 30.72z" fill="#FFE200" p-id="1527"></path><path d="M525.824 337.92l-45.568 12.288c3.584 8.192 7.68 17.92 11.264 27.136H354.816v69.12h43.52v-27.136h224.768v27.136h46.08V377.344h-124.928c-4.096-10.752-11.776-27.136-18.432-39.424z m8.704 90.624h-45.568v30.72h-112.64v183.296h42.496v-16.896h70.144V686.08h45.568v-59.904h69.12v15.36h44.544V459.776h-113.664v-31.232z m0 95.744v-27.136h69.12v27.136h-69.12z m-115.712 0v-27.136h70.144v27.136H418.816z m115.712 63.488v-27.136h69.12v27.136h-69.12z m-115.712 0v-27.136h70.144v27.136H418.816zM301.056 856.576c-5.632 0-11.264-1.536-16.896-5.12-10.752-7.168-21.504-14.336-31.232-22.528-16.384-12.8-31.744-27.136-46.08-42.496-11.776-12.288-10.752-31.744 1.536-43.52s31.744-10.752 43.52 1.536c12.288 12.8 25.088 25.088 39.424 35.84 8.704 6.656 17.408 13.312 26.624 18.944 14.336 9.216 18.432 28.16 9.216 42.496-6.144 9.728-15.872 14.848-26.112 14.848zM507.904 917.504c-30.72 0-61.44-3.584-91.648-10.24-15.36-3.584-30.72-7.68-45.056-13.312-15.872-5.632-24.576-23.04-18.944-39.424 5.632-15.872 23.04-24.576 39.424-18.944 12.8 4.608 25.6 8.192 38.4 11.264 25.6 5.632 51.712 8.704 77.824 8.704 16.896 0 30.72 13.824 30.72 30.72s-13.824 31.232-30.72 31.232zM596.992 907.264c-13.824 0-26.624-9.728-29.696-23.552-4.096-16.384 6.144-33.28 22.528-36.864 20.48-4.608 39.936-11.776 59.392-19.968 45.568-19.968 87.04-50.176 120.32-87.552 57.344-64 88.576-146.944 88.576-232.96 0-51.2-10.752-100.864-32.256-146.944-7.168-15.36-0.512-33.792 14.848-40.96 15.36-7.168 33.792-0.512 40.96 14.848 25.088 54.272 37.888 112.64 37.888 173.056 0 101.376-36.864 198.656-104.448 273.92-38.912 43.52-88.064 79.36-141.312 102.912-22.528 9.728-46.08 17.92-69.632 23.552-2.56 0-4.608 0.512-7.168 0.512z" fill="#4E63DD" p-id="1528"></path><path d="M164.352 700.928c-11.264 0-22.528-6.656-27.648-17.408-26.624-55.808-39.936-115.2-39.936-177.152 0-67.072 16.384-133.632 47.616-192 43.008-81.408 113.152-146.944 197.632-184.32C394.24 107.008 450.56 95.232 508.416 95.232c79.872 0 156.672 23.04 223.232 66.048 32.256 20.992 61.44 46.08 87.04 75.264 11.264 12.8 9.728 32.256-3.072 43.52s-32.256 9.728-43.52-3.072c-21.504-24.576-46.592-46.592-74.24-64-56.832-36.864-122.368-55.808-189.952-55.808-49.152 0-96.768 10.24-141.312 29.696-71.68 31.744-131.584 87.552-168.448 156.672-26.624 50.176-40.448 106.496-40.448 163.328 0 52.736 11.264 103.424 33.792 150.528 7.168 15.36 1.024 33.792-14.336 40.96-4.096 1.536-8.192 2.56-12.8 2.56z" fill="#4E63DD" p-id="1529"></path></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -1,10 +1,10 @@
<template>
<div class="nc-approve-container">
<el-card class="approve-card">
<div v-loading="loading" element-loading-text="信息加载中..." class="nc-approve-container">
<el-card v-if="showType === 'add'" class="approve-card">
<div slot="header" class="card-header">
<span class="header-title">
<i class="el-icon-document"></i>
信息审核
信息完善
</span>
<el-button type="primary" size="small" @click="handleSubmit" :loading="submitLoading"
:disabled="!canSubmit">
@ -47,15 +47,7 @@
</el-input>
</el-form-item>
</el-col>
<!-- <el-col :span="12">
<el-form-item label="审核状态" prop="audit_status">
<el-tag
:type="getStatusType(approveForm.audit_status)"
size="medium">
{{ getStatusText(approveForm.audit_status) }}
</el-tag>
</el-form-item>
</el-col> -->
</el-row>
<el-row :gutter="20">
@ -120,15 +112,9 @@
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="营业执照正本" prop="license_original_img">
<el-upload
class="license-uploader"
action="#"
:http-request="handleLicenseUpload"
:show-file-list="false"
:before-upload="beforeLicenseUpload"
accept="image/*">
<img v-if="approveForm.license_original_img"
:src="approveForm.license_original_img"
<el-upload class="license-uploader" action="#" :http-request="handleLicenseUpload"
:show-file-list="false" :before-upload="beforeLicenseUpload" accept="image/*">
<img v-if="approveForm.license_original_img" :src="approveForm.license_original_img"
class="license-image">
<i v-else class="el-icon-plus license-uploader-icon"></i>
</el-upload>
@ -138,17 +124,11 @@
</div>
</el-form-item>
</el-col>
<el-col :span="12">
<!-- <el-col :span="12">
<el-form-item label="营业执照副本" prop="license_copy_img">
<el-upload
class="license-uploader"
action="#"
:http-request="handleLicenseCopyUpload"
:show-file-list="false"
:before-upload="beforeLicenseUpload"
accept="image/*">
<img v-if="approveForm.license_copy_img"
:src="approveForm.license_copy_img"
<el-upload class="license-uploader" action="#" :http-request="handleLicenseCopyUpload"
:show-file-list="false" :before-upload="beforeLicenseUpload" accept="image/*">
<img v-if="approveForm.license_copy_img" :src="approveForm.license_copy_img"
class="license-image">
<i v-else class="el-icon-plus license-uploader-icon"></i>
</el-upload>
@ -157,7 +137,7 @@
支持 JPGPNGGIF 格式文件大小不超过 5MB
</div>
</el-form-item>
</el-col>
</el-col> -->
</el-row>
<!-- 操作按钮 -->
@ -178,9 +158,40 @@
</el-form-item>
</el-form>
</el-card>
<el-card v-if="showType === 'pending'" class="approve-card myStyle">
<div class="myStyle-content colStyle">
<span class="appTitle"> <img style="width: 50px;height: 50px;margin-right: 10px;" src="./img/apping.svg"
alt=""> 信息审核中</span>
<span class="tip">提交时间{{ currentInfo.create_at }}</span>
</div>
</el-card>
<el-card v-if="showType === 'rejected'" class="approve-card myStyle">
<div class="myStyle-content colStyle">
<span class="appTitle noapp"> <img style="width: 50px;height: 50px;margin-right: 10px;"
src="./img/noapp.svg" alt=""> 信息不通过</span>
<span class="noTip">驳回原因{{ currentInfo.reject_reason }} <span class="reapp"
@click="reApprove">重新提交</span></span>
</div>
</el-card>
<el-card v-if="showType === 'approved'" class="approve-card myStyle">
<div class="myStyle-content colStyle appredStyle">
<div class="appredStyle-content">
<span class="smallTitle"> 大陆资源审核通过</span>
<img src="./img/app.png" alt="">
</div>
<div class="appredStyle-content">
<span class="tip" style="color: #67c23a;">审核状态已通过</span>
<span class="tip">审核时间{{ currentInfo.update_time }}</span>
</div>
</div>
</el-card>
<!-- 预览对话框 -->
<el-dialog title="审批信息预览" :visible.sync="previewVisible" top="3vh" width="60%" :before-close="handlePreviewClose">
<el-dialog title="审批信息预览" :visible.sync="previewVisible" top="3vh" width="60%"
:before-close="handlePreviewClose">
<div class="preview-content">
<el-descriptions :column="2" border>
<el-descriptions-item label="账号类型">
@ -192,11 +203,6 @@
<el-descriptions-item label="执照号码">
{{ approveForm.license_number }}
</el-descriptions-item>
<!-- <el-descriptions-item label="审核状态1">
<el-tag :type="getStatusType(approveForm.audit_status)">
{{ getStatusText(approveForm.audit_status) }}
</el-tag>
</el-descriptions-item> -->
<el-descriptions-item label="办公地址">
{{ approveForm.office_address }}
</el-descriptions-item>
@ -219,10 +225,10 @@
<h4>营业执照正本</h4>
<img :src="approveForm.license_original_img" alt="营业执照正本" class="preview-license-img">
</div>
<div class="preview-image" v-if="approveForm.license_copy_img">
<!-- <div class="preview-image" v-if="approveForm.license_copy_img">
<h4>营业执照副本</h4>
<img :src="approveForm.license_copy_img" alt="营业执照副本" class="preview-license-img">
</div>
</div> -->
</div>
</div>
<span slot="footer" class="dialog-footer">
@ -233,11 +239,14 @@
</div>
</template>
<script>
import {reqApproveUser } from '@/api/ncmatch/index'
import { reqApproveUser, reqApproveUserSearch } from '@/api/ncmatch/index'
export default {
name: "ncApprove",
data() {
return {
loading: true,
showType: '',//add pending rejected approved
currentInfo: {},
approveForm: {
account_type: "", //
company_name: '', //
@ -289,19 +298,6 @@ export default {
trigger: 'change'
}
],
license_copy_img: [
{
required: true,
validator: (rule, value, callback) => {
if (!this.approveForm.license_copy_file) {
callback(new Error('请上传营业执照副本'));
} else {
callback();
}
},
trigger: 'change'
}
]
},
submitLoading: false,
previewVisible: false
@ -315,11 +311,41 @@ export default {
this.approveForm.contact_name &&
this.approveForm.mobile_phone &&
this.approveForm.email &&
this.approveForm.license_original_file &&
this.approveForm.license_copy_file;
this.approveForm.license_original_file;
}
},
created() {
this.getApproveInfo()
},
methods: {
reApprove(){
this.showType = 'add'
this.approveForm = this.currentInfo
},
getApproveInfo() {
reqApproveUserSearch({ url_link: window.location.href }).then(res => {
this.loading = false
if(res.data.data.length===0){
this.showType='add'
}else if(res.data.data[0].audit_status==='pending'){
this.currentInfo=res.data.data[0]
this.showType='pending'
}else if(res.data.data[0].audit_status==='rejected'){
this.currentInfo=res.data.data[0]
this.showType='rejected'
}else if(res.data.data[0].audit_status==='approved'){
this.currentInfo=res.data.data[0]
this.showType='approved'
}
}).catch(error => {
this.loading = false
console.error('获取审批信息失败:', error)
this.$message.error('获取审批信息失败,请刷新页面重试')
//
this.showType = 'add'
})
},
//
getAccountTypeText(type) {
const typeMap = {
@ -424,15 +450,12 @@ export default {
formData.append('license_original_img', this.approveForm.license_original_file);
}
if (this.approveForm.license_copy_file) {
formData.append('license_copy_img', this.approveForm.license_copy_file);
}
reqApproveUser(formData).then(res => {
this.submitLoading = false;
if (res.status) {
this.$message.success('审批信息提交成功!');
this.approveForm.audit_status = 'processing';
this.approveForm.audit_status = 'pending';
this.getApproveInfo()
} else {
this.$message.error(res.message || '提交失败,请重试');
}
@ -451,9 +474,7 @@ export default {
handleReset() {
this.$refs.approveForm.resetFields();
this.approveForm.license_original_img = '';
this.approveForm.license_copy_img = '';
this.approveForm.license_original_file = null;
this.approveForm.license_copy_file = null;
this.approveForm.audit_status = 'pending';
this.$message.info('表单已重置');
},
@ -487,9 +508,30 @@ export default {
</script>
<style lang="scss" scoped>
.appredStyle{
border: 1px solid #f2f2fb;
width: 500px;
height: 220px;
display: flex;
flex-direction: row!important;
align-items: center;
justify-content: space-around!important;
.appredStyle-content{
// border: 5px solid red;
display: flex;
flex-direction: column;
align-items: flex-start!important;
justify-content:center;
}
}
.nc-approve-container {
min-height: 100vh;
box-sizing: border-box;
height: calc(100vh - 60px);
position: relative;
display: flex;
width: 100%;
justify-content: center;
align-items: flex-start;
&::before {
content: '';
@ -504,7 +546,7 @@ export default {
}
.approve-card {
width: 100%;
// max-width: 1200px;
margin: 0 45px;
background: rgba(255, 255, 255, 0.95);
@ -630,7 +672,7 @@ export default {
cursor: pointer;
position: relative;
overflow: hidden;
width:150px;
width: 150px;
height: 150px;
display: flex;
align-items: center;
@ -942,4 +984,63 @@ export default {
background: linear-gradient(135deg, #5a6fd8, #6a4190);
}
}
.myStyle {
box-sizing: border-box;
width: 100%;
height: calc(100vh - 100px);
display: flex;
justify-content: center;
align-items: center;
font-size: 20px;
font-weight: bold;
color: #667eea;
}
.colStyle {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.appTitle {
display: flex;
align-items: center;
justify-content: center;
}
.tip {
font-size: 12px;
font-weight: 400;
color: #667eea;
margin-top: 10px;
}
.noTip {
font-size: 12px;
font-weight: 400;
color: #f56c6c;
margin-top: 10px;
}
.noapp {
color: #f56c6c;
}
.reapp {
font-size: 12px;
font-weight: 400;
color: #667eea;
margin-top: 10px;
cursor: pointer !important;
}
.smallTitle{
font-size: 14px;
font-weight: 600;
color: #83858a;
margin-bottom: 10px;
}
</style>

View File

@ -22,7 +22,7 @@
</div>
<div class="table-box">
<div style="margin-bottom: 10px;">
<el-radio-group v-if="role.role_type == 'customer'" v-model="searchData.radioType" class="radio-group"
<el-radio-group v-if="role.type == 'app'" v-model="searchData.radioType" class="radio-group"
size="mini" @change="handleRadioChange">
<el-radio-button v-for="item in radioOptions" :key="item.value" :label="item.value">
{{ item.label }}
@ -32,24 +32,24 @@
<i class="el-icon-plus"></i> 添加{{ searchData.radioType === '1' || searchData.radioType === '3' ?
'需求' : '商品' }}
</el-button> -->
<!-- <el-button style="margin-left: 10px;" size="mini" @click="exportData">
<i class="el-icon-upload2"></i> 导出
</el-button> -->
<el-button style="margin-left: 10px;" size="mini" @click="getTableData('1')">
<i class="el-icon-upload2"></i> 全部导出
</el-button>
</div>
<el-table border height="calc(100vh - 210px)" v-loading="loading" :data="tableData"
<el-table size="mini" border height="calc(100vh - 210px)" v-loading="loading" :data="tableData"
style="width: 100%;border:1px solid #ccc;" element-loading-text="加载中..."
element-loading-spinner="el-icon-loading" element-loading-background="rgba(255, 255, 255, 0.8)">
<el-table-column prop="date" label="商品图片" width="180">
<el-table-column prop="date" label="图片" width="180">
<template slot-scope="scope">
<el-image :src="scope.row.img || 'https://www.kaiyuancloud.cn/idfile?path=logo_ncmatch.png'"
<el-image v-if="scope.row.img" :src="scope.row.img || 'https://www.kaiyuancloud.cn/idfile?path=logo_ncmatch.png'"
style="width: 80px; height: 60px;border: 1px solid #ccc;border-radius: 6px;"
:preview-src-list="[scope.row.img || 'https://www.kaiyuancloud.cn/idfile?path=logo_ncmatch.png']"
fit="cover" :initial-index="0" preview-teleported :z-index="3000">
</el-image>
<span v-else>/</span>
</template>
</el-table-column>
<el-table-column prop="product_name" show-overflow-tooltip label="商品名称" min-width="180">
<el-table-column prop="product_name" show-overflow-tooltip label="名称" min-width="180">
</el-table-column>
<el-table-column prop="product_category" show-overflow-tooltip label="所属类别" min-width="180">
</el-table-column>
@ -72,31 +72,25 @@
</el-table-column>
<el-table-column prop="company_type" show-overflow-tooltip label="公司类别" min-width="180">
</el-table-column>
<!-- <el-table-column prop="requirement_summary" show-overflow-tooltip label="商品概述" min-width="180">
</el-table-column>
<el-table-column prop="related_parameters" show-overflow-tooltip label="相关参数" min-width="180">
</el-table-column> -->
<el-table-column prop="contact_person" show-overflow-tooltip label="联系人" min-width="180">
</el-table-column>
<el-table-column prop="phone_number" show-overflow-tooltip label="联系电话" min-width="180">
</el-table-column>
<el-table-column prop="create_at" show-overflow-tooltip label="创建时间" min-width="180">
</el-table-column>
<el-table-column fixed="right" min-width="240" label="操作">
<el-table-column fixed="right" min-width="260" label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="openDetail(scope.row, 1)">查看</el-button>
<el-button @click="editProduct(scope.row)" type="text" style="color: #409EFF;"
size="small">修改</el-button>
<el-button v-if="role.role_type == 'customer'" type="text" size="small"
@click="openDetail(scope.row, 2)">审核</el-button>
<!-- <el-button type="text" style="color: #E6A23C;" size="small">导出</el-button> -->
<el-button
:disabled="scope.row.audit_status === 'approved' && scope.row.listing_status === 'listing'"
:disabled="scope.row.audit_status=='rejected'||scope.row.audit_status === 'approved' && scope.row.listing_status === 'listing'"
@click="grounding(scope.row, 'listing')" type="text" style="color: #67C23A;"
size="small">上架</el-button>
<el-button
:disabled="scope.row.audit_status === 'approved' && scope.row.listing_status === 'delisting'"
:disabled="scope.row.audit_status=='rejected'||scope.row.audit_status === 'approved' && scope.row.listing_status === 'delisting'"
@click="grounding(scope.row, 'delisting')" type="text" size="small">下架</el-button>
<el-button type="text" size="small" @click="exportData(scope.row)">导出</el-button>
<el-popconfirm v-if="role.role_type == 'customer'" title="确定删除该条目吗?"
@ -104,8 +98,6 @@
<el-button slot="reference" type="text" style="color: #F56C6C;margin-left: 10px;"
size="small">删除</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
@ -281,6 +273,9 @@ export default {
}
},
methods: {
exportAllData(){
console.log("全部导出");
},
deleteProduct(row) {
console.log(row);
let ploay = {
@ -341,8 +336,8 @@ export default {
},
exportData(row) {
let ploay = {
// ids: [row.id]
ids:["fYHuG9eXwLi7949Rg9mRg", "vTv8KuqfDhT1efC8prs9y"]
ids: [row.id]
// ids:["fYHuG9eXwLi7949Rg9mRg", "vTv8KuqfDhT1efC8prs9y"]
}
reqExportProduct(ploay).then(res => {
console.log(res);
@ -393,7 +388,7 @@ export default {
this.closeEditDialog();
this.getTableData(); //
},
getTableData() {
getTableData(to_excel) {
//
if (this.searchDate && this.searchDate.length === 2) {
this.searchData.start_date = this.searchDate[0];
@ -406,6 +401,25 @@ export default {
console.log("searchData", this.searchData);
this.loading = true;
if(to_excel==='1'){
this.searchData.to_excel=to_excel
reqSearchByMangement({url_link:window.location.href,to_excel:to_excel,publish_type:this.searchData.publish_type}).then(res => {
console.log(res);
if (res.status) {
// Workbook
const wb = XLSX.utils.book_new();
// sheet
const ws1 = XLSX.utils.json_to_sheet(res.data);
XLSX.utils.book_append_sheet(wb, ws1, 'sheet1');
// Excel
XLSX.writeFile(wb, '产品列表.xlsx');
}
}).catch(error => {
console.error('获取数据失败:', error);
}).finally(() => {
this.loading = false;
})
}else{
reqSearchByMangement(this.searchData).then(res => {
console.log(res);
if (res.status) {
@ -417,6 +431,8 @@ export default {
}).finally(() => {
this.loading = false;
})
}
},
//
handleSizeChange(val) {

View File

@ -0,0 +1,28 @@
<template>
<div>
<qualificationBox :approveInfo="approveInfo"></qualificationBox>
</div>
</template>
<script>
import qualificationBox from '../qualificationBox/index.vue';
export default {
name: 'apprvedInfo',
components: {
qualificationBox
},
data() {
return {
approveInfo:{
audit_status:"approved,rejected",
}
}
},
created(){
this.initData()
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,13 @@
<template>
<div>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'qualificationReview',
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,28 @@
<template>
<div>
<qualificationBox :approveInfo="approveInfo"></qualificationBox>
</div>
</template>
<script>
import qualificationBox from '../qualificationBox/index.vue';
export default {
name: 'noApproveInfo',
components: {
qualificationBox
},
data() {
return {
approveInfo:{
audit_status:"pending",
}
}
},
created() {
},
}
</script>

View File

@ -0,0 +1,379 @@
<template>
<div>
<el-table
:data="tableData"
style="width: 100%">
<el-table-column
prop="date"
label="图片"
width="180">
<template slot-scope="scope">
<el-image v-if="scope.row.license_original_img" :src="scope.row.license_original_img "
style="width: 80px; height: 60px;border: 1px solid #ccc;border-radius: 6px;"
:preview-src-list="[scope.row.license_original_img]"
fit="cover" :initial-index="0" preview-teleported :z-index="3000">
</el-image>
<span v-else>/</span>
</template>
</el-table-column>
<el-table-column
prop="contact_name"
label="联系人"
min-width="180">
</el-table-column>
<el-table-column
prop="mobile_phone"
label="联系电话"
min-width="180">
</el-table-column>
<el-table-column
prop="email"
label="邮箱"
min-width="180">
</el-table-column>
<el-table-column
prop="telephone"
label="公共电话"
min-width="180">
</el-table-column>
<el-table-column
prop="audit_status"
label="审核状态"
min-width="180">
<template slot-scope="scope">
<el-tag :type="getStatusType(scope.row.audit_status)">
{{ getStatusText(scope.row.audit_status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="address"
label="操作"
fixed="right"
width="180">
<template slot-scope="scope">
<el-button type="text" @click="handleAudit(scope.row)" size="small">审核</el-button>
</template>
</el-table-column>
</el-table>
<!-- 预览对话框 -->
<el-dialog title="审批信息预览" :visible.sync="previewVisible" top="3vh" width="60%"
:before-close="handlePreviewClose">
<div class="preview-content">
<el-descriptions :column="2" border>
<el-descriptions-item label="账号类型">
{{ getAccountTypeText(approveForm.account_type) }}
</el-descriptions-item>
<el-descriptions-item label="公司名称">
{{ approveForm.company_name }}
</el-descriptions-item>
<el-descriptions-item label="执照号码">
{{ approveForm.license_number }}
</el-descriptions-item>
<!-- <el-descriptions-item label="审核状态1">
<el-tag :type="getStatusType(approveForm.audit_status)">
{{ getStatusText(approveForm.audit_status) }}
</el-tag>
</el-descriptions-item> -->
<el-descriptions-item label="办公地址">
{{ approveForm.office_address }}
</el-descriptions-item>
<el-descriptions-item label="注册地址">
{{ approveForm.registered_address }}
</el-descriptions-item>
<el-descriptions-item label="联系人">
{{ approveForm.contact_name }}
</el-descriptions-item>
<el-descriptions-item label="联系电话">
{{ approveForm.mobile_phone }}
</el-descriptions-item>
<el-descriptions-item label="邮箱地址">
{{ approveForm.email }}
</el-descriptions-item>
</el-descriptions>
<div class="preview-images">
<div class="preview-image" >
<h4>营业执照正本</h4>
<el-image :src="approveForm.license_original_img "
class="preview-license-img"
:preview-src-list="[approveForm.license_original_img]"
fit="cover" :initial-index="0" preview-teleported :z-index="3000">
</el-image>
</div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button size="mini" @click="previewVisible = false"> </el-button>
<el-button size="mini" type="danger" @click="handleReject">审核不通过</el-button>
<el-button size="mini" type="success" @click="handleApprove">审核通过</el-button>
</span>
</el-dialog>
<!-- 驳回理由输入弹窗 -->
<el-dialog title="审核退回" :visible.sync="rejectDialogVisible" width="40%" @close="handleRejectDialogClose">
<el-form :model="rejectForm" :rules="rejectRules" ref="rejectForm">
<el-form-item label="驳回原因" prop="reject_reason" :label-width="formLabelWidth">
<el-input
v-model="rejectForm.reject_reason"
type="textarea"
:rows="4"
placeholder="请输入驳回原因"
maxlength="500"
show-word-limit>
</el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="rejectDialogVisible = false"> </el-button>
<el-button type="primary" @click="confirmReject" :loading="rejectLoading"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { reqEnterpriseUpdate,reqApproveUserSearch } from '@/api/ncmatch';
export default {
name: 'qualificationBox',
data() {
return {
current_page:1,
page_size:10,
total:0,
approveForm:{},
previewVisible: false,
rejectDialogVisible: false,
rejectLoading: false,
formLabelWidth: '120px',
rejectForm: {
reject_reason: ''
},
rejectRules: {
reject_reason: [
{ required: true, message: '请输入驳回原因', trigger: 'blur' },
{ min: 5, max: 500, message: '驳回原因长度在 5 到 500 个字符', trigger: 'blur' }
]
},
tableData: []
}
},
props:{
approveInfo:{
type:Object,
default:()=>{}
}
},
created(){
this.initData()
},
methods: {
initData(){
let ploay = {
current_page:this.current_page,
page_size:this.page_size,
audit_status:this.approveInfo.audit_status,
url_link:window.location.href,
}
reqApproveUserSearch(ploay).then(res=>{
if(res.status){
this.tableData = res.data
this.total = res.data.total_count
this.tableData = res.data.data
}else{
this.$message.error(res.msg || '加载失败,请重试');
}
})
},
handleAudit(row) {
console.log(row)
this.previewVisible = true
this.approveForm = row
},
//
handleApprove() {
this.$confirm('确认审核通过该资格申请吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.approveQualification();
}).catch(() => {
this.$message.info('已取消操作');
});
},
//
handleReject() {
this.rejectDialogVisible = true;
//
this.rejectForm.reject_reason = this.approveForm.reject_reason || '';
//
this.$nextTick(() => {
if (this.$refs.rejectForm) {
this.$refs.rejectForm.clearValidate();
}
});
},
//
confirmReject() {
this.$refs.rejectForm.validate((valid) => {
if (valid) {
this.rejectQualification();
}
});
},
//
approveQualification() {
// API
const params = {
id: this.approveForm.id,
audit_status: 'approved'
};
console.log("审核通过",params)
reqEnterpriseUpdate(params).then(res=>{
if(res.status){
this.$message.success('审核通过成功');
this.initData()
this.previewVisible = false;
this.refreshTable();
this.initData();
}else{
this.$message.error(res.msg || '审核失败,请重试');
}
}).catch(err=>{
this.$message.error('审核失败,请重试');
})
// API使API
// API
// this.$api.qualification.approve(params).then(res => {
// if (res.status) {
// this.$message.success('');
// this.previewVisible = false;
// this.refreshTable();
// } else {
// this.$message.error(res.msg || '');
// }
// }).catch(err => {
// this.$message.error('');
// });
},
//
rejectQualification() {
this.rejectLoading = true;
const params = {
id: this.approveForm.id,
audit_status: 'rejected',
reject_reason: this.rejectForm.reject_reason
};
console.log("审核驳回",params)
reqEnterpriseUpdate(params).then(res=>{
if(res.status){
this.rejectLoading = false;
this.$message.success('审核驳回成功');
this.rejectDialogVisible = false;
this.previewVisible = false;
this.initData();
}else{
this.$message.error(res.msg || '审核失败,请重试');
}
}).catch(err=>{
this.$message.error('审核失败,请重试');
})
// API
// this.$api.qualification.reject(params).then(res => {
// this.rejectLoading = false;
// if (res.status) {
// this.$message.success('');
// this.rejectDialogVisible = false;
// this.previewVisible = false;
// this.refreshTable();
// } else {
// this.$message.error(res.msg || '');
// }
// }).catch(err => {
// this.rejectLoading = false;
// this.$message.error('');
// });
},
//
refreshTable() {
//
console.log('刷新表格数据');
},
//
handleRejectDialogClose() {
this.rejectDialogVisible = false;
this.rejectForm.reject_reason = '';
//
this.$nextTick(() => {
if (this.$refs.rejectForm) {
this.$refs.rejectForm.clearValidate();
}
});
},
//
handlePreviewClose() {
this.previewVisible = false;
},
//
getAccountTypeText(type) {
const typeMap = {
'enterprise': '企业账号',
'personal': '个人账号',
'government': '政府机构'
};
return typeMap[type] || '未知';
},
//
getStatusType(status) {
const statusMap = {
'pending': 'warning',
'approved': 'success',
'rejected': 'danger',
'processing': 'info'
};
return statusMap[status] || 'info';
},
//
getStatusText(status) {
const statusMap = {
'pending': '待审核',
'approved': '已通过',
'rejected': '已拒绝',
'processing': '审核中'
};
return statusMap[status] || '未知';
},
}
}
</script>
<style scoped lang="less">
.preview-license-img{
width: 300px;
height: 200px;
border-radius: 16px;
border: 1px solid #ccc;
transition: transform 0.3s ease;
&:hover{
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
transform: translateY(-3px);
transition: transform 0.3s ease;
}
}
</style>

View File

@ -1,7 +1,8 @@
<template>
<div class="top-nav">
<div id="topContainer" class="container">
<div class="logo">
<div id="topContainer" class="container" :class="{ 'ncmatch-layout': homePath === '/ncmatchHome/index' }">
<!-- 当homePath不等于/ncmatchHome/index时显示logo和导航 -->
<div class="logo" v-if="homePath !== '/ncmatchHome/index'">
<img v-if="JSON.stringify(logoInfoNew)!=='{}'" @click="$router.push(homePath)"
style="cursor:pointer;margin-right: 71px" class="logoImg"
:src=" logoInfoNew.home.logoImg || '' "
@ -29,9 +30,81 @@
</nav>
</div>
<!-- 当homePath等于/ncmatchHome/index时的布局 -->
<div class="logo" v-else>
<!-- 登录状态用户头像在最左侧 -->
<el-dropdown v-if="loginState" @visible-change="handleShow"
class="avatar-container left-menu-item hover-effect nick-name-style"
style="margin-right: 0"
trigger="click">
<div class="avatar-wrapper">
<i
style="font-size: 18px; color: #262626;font-weight: 400;cursor: pointer;display: flex;justify-content: flex-start;align-items: center">
<div class="imgUser">{{ nick_name.charAt(0) }}</div>
{{ nick_name }}
<i id="resverIcon" class="el-icon-arrow-up el-icon--right resverIcon"></i>
</i>
</div>
<el-dropdown-menu slot="dropdown" style="width: 230px;font-size: 16px;" divided>
<div
style="display: flex;flex-direction: column;padding:15px 15px;background-color: #f0f2f5">
<span style="font-weight: bold;font-size: 20px;margin-bottom: 5px;color: #333333">{{ nick_name }}</span>
<span
style="font-size: 12px;color:#666;display: flex;justify-content:flex-start;align-items: center"> ID: <span
ref="contentToCopy">
{{
userId
}}
</span>
<svg
@click="copyBtn"
class="copy-btn"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
width="12"
height="12"
style="fill: #1019ab;"
>
<path
d="M394.666667 106.666667h448a74.666667 74.666667 0 0 1 74.666666 74.666666v448a74.666667 74.666667 0 0 1-74.666666 74.666667H394.666667a74.666667 74.666667 0 0 1-74.666667-74.666667V181.333333a74.666667 74.666667 0 0 1 74.666667-74.666666z m0 64a10.666667 10.666667 0 0 0-10.666667 10.666666v448a10.666667 10.666667 0 0 0 10.666667 10.666667h448a10.666667 10.666667 0 0 0 10.666666-10.666667V181.333333a10.666667 10.666667 0 0 0-10.666666-10.666666H394.666667z m245.333333 597.333333a32 32 0 0 1 64 0v74.666667a74.666667 74.666667 0 0 1-74.666667 74.666666H181.333333a74.666667 74.666667 0 0 1-74.666666-74.666666V394.666667a74.666667 74.666667 0 0 1 74.666666-74.666667h74.666667a32 32 0 0 1 0 64h-74.666667a10.666667 10.666667 0 0 0-10.666666 10.666667v448a10.666667 10.666667 0 0 0 10.666666 10.666666h448a10.666667 10.666667 0 0 0 10.666667-10.666666v-74.666667z"
p-id="1521"></path>
</svg>
</span>
</div>
<el-divider style="margin: 0!important;"></el-divider>
<div
v-if="role.includes('客户')"
style="padding: 0 18px; width: 100%;height: 60px;display: flex;justify-content: space-between;align-items: center">
<span style="color:#666;display: block;font-size: 14px;"
class="moneyNow"><span style="display: block;margin-bottom: 5px">余额</span>{{
mybalance ? mybalance : initMybalance()
}} </span>
<el-button v-if="!isShowKbossCharge" type="primary" @click="$router.push('/kbossCharge')" plain
style="padding: 8px; ">
充值
</el-button>
</div>
<el-dropdown-item v-if="role.includes('客户')"
@click.native="$router.push('/customer/customerInformation')">
<span style="display: block">个人中心</span>
</el-dropdown-item>
<el-dropdown-item @click.native="logout">
<span style="display: block">退出登录</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<!-- 公司名称在中间 -->
<div class="company-name" @click="$router.push(homePath)" style="cursor:pointer;font-size: 24px;font-weight: bold;color: #222F60;margin: 0 auto;">
{{ (logoInfoNew.home && logoInfoNew.home.orgName) || '' }}
</div>
</div>
<div class="user-area">
<a @click="$router.push('/product/productHome')" v-if="loginState" class="login-btn">控制台</a>
<a @click="goB" v-if="loginState" class="login-btn">控制台</a>
<a @click="$router.push({
path:'/login',
query:{
@ -46,7 +119,7 @@
<!-- <span style="text-decoration: underline; text-decoration-color: #1b5bff" v-if="!showRegisterButton">{{-->
<!-- username-->
<!-- }}</span>-->
<el-dropdown v-if="loginState " @visible-change="handleShow"
<el-dropdown v-if="loginState && homePath !== '/ncmatchHome/index'" @visible-change="handleShow"
class="avatar-container right-menu-item hover-effect nick-name-style"
style="margin-right: 0"
trigger="click">
@ -202,7 +275,6 @@ import {mapGetters, mapState} from "vuex";
import {getLogoAPI, getUserInfoAPI} from "@/api/login";
import {reqApplyChannel} from "@/api/customer/channel";
import store from "@/store";
import { windows } from 'codemirror/src/util/browser';
import { getHomePath } from '@/views/setting/tools'
export default Vue.extend({
name: "TopBox",
@ -226,6 +298,7 @@ export default Vue.extend({
}
},
created() {
this.homePath = getHomePath()
if (sessionStorage.getItem('userId')) {
this.$store.commit('setLoginState', true); // Vuex
}
@ -306,6 +379,13 @@ export default Vue.extend({
// },
methods: {
goB() {
if (sessionStorage.getItem('jueseNew').includes('客户')) {
this.$router.push('/product/productHome')
} else if (sessionStorage.getItem('jueseNew').includes('运营')) {
this.$router.push('/operation/supplierManagement')
}
},
init() {
// let params = {
// url_link: this.GetQueryString(window.location.href)
@ -398,7 +478,7 @@ export default Vue.extend({
localStorage.removeItem('userId')
if (!window.location.href.includes('homePage/index')) {
await this.$router.push(`/homePage/index`);
await this.$router.push(getHomePath());
}
},
edituserPassword() {//
@ -979,4 +1059,41 @@ export default Vue.extend({
background: #081020;
color: white;
}
.company-name {
font-size: 24px;
font-weight: bold;
color: #222F60;
cursor: pointer;
transition: color 0.3s ease;
&:hover {
color: #1E6FFF;
}
}
/* 当homePath等于/ncmatchHome/index时的特殊样式 */
.top-nav .container {
&.ncmatch-layout {
.logo {
flex: 1;
justify-content: space-between;
align-items: center;
}
.user-area {
justify-content: flex-end;
flex-shrink: 0;
}
}
}
/* 左侧用户头像样式 */
.left-menu-item {
.avatar-wrapper {
i {
justify-content: flex-start !important;
}
}
}
</style>

View File

@ -100,6 +100,7 @@ export default Vue.extend({
this.$refs['ruleForm'].validate((valid) => {
if (valid) {
this.addBtnLoading = true
this.addData.url_link = window.location.href
reqNewHomeConsult(this.addData).then(response => {
this.addBtnLoading = false
if (response.status) {

View File

@ -1,6 +1,6 @@
<script >
<script>
import Vue from 'vue'
import { reqPublishProductSearchFirstPage } from '@/api/ncmatch'
import { reqPublishProductSearchFirstPage, reqEnterpriseAuditInfoSearch, reqHomepageProductCategory, reqGetSupplyAndDemandSquareList } from '@/api/ncmatch'
import { mapGetters, mapState } from "vuex";
export default Vue.extend({
name: "mainPage",
@ -11,10 +11,14 @@ export default Vue.extend({
menuAside: () => import('./menuAside/index.vue')
},
created() {
this.init_product_list()
this.getHomepageProductCategory()
},
data() {
return {
boxLoading:false,
selectedCategory: "",
showTip: false,
total: 0,
publish_type: null,
sendProductVisible: false,
@ -143,18 +147,73 @@ export default Vue.extend({
},
methods: {
getHomepageProductCategory() {
reqHomepageProductCategory({
url_link: window.location.href,
publish_type: "1"
}).then(res => {
if (res.status) {
this.categories = res.data
this.product = res.data
this.currentHotMenu = res.data[0].id
this.selectedCategory = res.data[0].id
this.initData()
}
})
},
goInfo() {
this.showTip = false
this.$router.push('/customer/approve')
},
handleCurrentChange(val) {
this.current_page = val
this.init_product_list()
this.initData()
// this.init_product_list()
},
sendInfo(type) {
if (this.loginState) {
reqEnterpriseAuditInfoSearch({
url_link: window.location.href,
}).then(res => {
if (res.data.length !== 0 || !sessionStorage.getItem('juese').includes('客户')) {
this.publish_type = type
this.sendProductVisible = true
} else {
this.showTip = true
}
})
} else {
this.$router.push('/login')
}
},
initData() {
this.boxLoading = true
let ploay = {
product_category: this.selectedCategory,
to_page: "first_page",
url_link: window.location.href,
page_size: this.page_size,
current_page: this.current_page,
publish_type: "1"
}
reqGetSupplyAndDemandSquareList(ploay).then(res => {
this.boxLoading = false
if (res.status) {
if (res.data.length === 0) {
this.hotProductList = []
this.total = 0
} else {
this.hotProductList = res.data[0].product_list
this.total = res.data[0].total_count
}
}
})
},
init_product_list() {
reqPublishProductSearchFirstPage({ publish_type: "1", url_link: window.location.href, to_page: 'first_page', page_size: this.page_size, current_page: this.current_page, product_type: this.currentHotMenu }).then(res => {
if (res.status) {
@ -175,7 +234,9 @@ export default Vue.extend({
clickNetMenu(menu) {
this.current_page = 1
this.currentHotMenu = menu.id;
this.hotProductList = menu.product_list || [];
this.selectedCategory = menu.id
this.initData()
// this.hotProductList = menu.product_list || [];
},
handleSearch() {
console.log('搜索:1', this.searchKeyword)
@ -224,11 +285,11 @@ export default Vue.extend({
<span class="publish-goods" @click="sendInfo('2')">发布需求</span>
<span class="publish-goods" @click="sendInfo('1')">发布商品</span>
<ul class="userBtn">
<!-- <ul class="userBtn">
<li><img src="./img/eye.png" alt="">浏览记录</li>
<li><img src="./img/collect.png" alt="">收藏商品</li>
<li><img src="./img/like.png" alt="">关注需求</li>
</ul>
</ul> -->
</aside>
</div>
</main>
@ -237,12 +298,12 @@ export default Vue.extend({
<li v-for="menu in product" :class="currentHotMenu === menu.id ? 'activeMenu' : ''" @click="clickNetMenu(menu)"
:key="menu.name">
<!-- <img :src="currentHotMenu === menu.id ? menu.activeIcon : menu.icon" alt="" /> -->
<span class="tab-text">{{ menu.name }}</span>
<span class="tab-text">{{ menu.product_category }}</span>
</li>
</ul>
<productCard :productList="hotProductList"></productCard>
<productCard v-loading="boxLoading" :productList="hotProductList"></productCard>
<el-pagination style="background-color: white;" @current-change="handleCurrentChange" :page-size="page_size"
layout="total, prev, pager, next" :total="total">
</el-pagination>
@ -251,6 +312,14 @@ export default Vue.extend({
<sendProduct :isEdit="false" v-if="publish_type" @success="sendProductSuccess" :publish_type="publish_type">
</sendProduct>
</el-dialog>
<el-dialog title="温馨提示" :visible.sync="showTip" width="30%">
<span>您还没有完善企业信息完善企业信息审核通过后您可以发布需求与商品</span>
<span slot="footer" class="dialog-footer">
<span> <span style="margin-right: 10px;"> 跳转到</span> <el-button size="small" type="primary"
@click="goInfo">信息完善</el-button></span>
</span>
</el-dialog>
</div>
</div>
</template>
@ -501,7 +570,7 @@ export default Vue.extend({
//
.main-content {
padding: 16px;
// padding: 16px;
width: 100%;
max-width: 1600px;

View File

@ -10,8 +10,7 @@
<span class="category-name">{{ category.first_level_name }}</span>
<span style="display: flex;margin-left: 0px;padding-left: 0px;">|</span>
<div class="menu-item">
<span v-for="(secondary, index) in category.secondaryClassification"
:key="secondary.id">
<span v-for="(secondary, index) in category.secondaryClassification" :key="secondary.id">
{{ secondary.second_level_name }}{{ index < category.secondaryClassification.length - 1 ? ' / '
: '' }} </span>
</div>
@ -19,13 +18,14 @@
</ul>
<transition name="slide-fade">
<!-- v-if="currentCategory" -->
<div v-loading="loading" element-loading-text="加载中..." element-loading-spinner="el-icon-loading" element-loading-background="rgba(255, 255, 255, 0.8)" class="rightBox" v-if="currentCategory" @mouseenter="keepProductList" @mouseleave="hideProductList">
<div v-loading="loading" element-loading-text="加载中..." element-loading-spinner="el-icon-loading"
element-loading-background="rgba(255, 255, 255, 0.8)" class="rightBox" v-if="currentCategory"
@mouseenter="keepProductList" @mouseleave="hideProductList">
<div class="rightBox-content">
<!-- 二级菜单标题 -->
<div class="secondary-menu">
<div v-for="secondary in currentCategory.secondaryClassification"
:key="secondary.id" class="secondary-item"
:class="{ active: selectedSecondary === secondary }"
<div v-for="secondary in currentCategory.secondaryClassification" :key="secondary.id"
class="secondary-item" :class="{ active: selectedSecondary === secondary }"
@mouseenter="selectSecondary(secondary)">
{{ secondary.second_level_name }}
</div>
@ -47,14 +47,15 @@
<!-- 如果有四级菜单product_list直接显示所有四级菜单项 -->
<div v-if="third.product_list && third.product_list.length > 0"
class="product-grid">
<div v-for="(product, index) in third.product_list"
:key="product.id" class="product-tag">
<div @click="openTalk" v-for="(product, index) in third.product_list"
:key="product.id"
class="product-tag">
{{ product.first_level_name }}
</div>
</div>
<!-- 如果没有四级菜单将三级菜单项视为四级菜单项显示 -->
<div v-else class="product-grid">
<div class="product-tag ">
<div @click="openTalk" class="product-tag ">
{{ third.third_level_name }}
</div>
</div>
@ -81,8 +82,8 @@
<!-- 默认显示所有二级菜单项 -->
<div v-else class="jd-style-menu">
<div v-for="secondary in currentCategory.secondaryClassification"
:key="secondary.id" class="category-section">
<div v-for="secondary in currentCategory.secondaryClassification" :key="secondary.id"
class="category-section">
<div class="section-header">
<span class="section-title">{{ secondary.second_level_name }}</span>
<span class="section-arrow">></span>
@ -100,19 +101,24 @@
</div>
</div>
</transition>
<Talk></Talk>
</aside>
</template>
<script>
import { reqNcMatchMenu } from '@/api/ncmatch';
import { buildDynamicStructure } from './buildNcmatchTree';
import Talk from '@/views/homePage/dialog/talk/index.vue';
export default {
name: 'menuAside',
components: {
Talk
},
data() {
return {
currentCategory: null,
selectedSecondary: null,
hideTimer: null,
categories: [ ],
categories: [],
loading: false,
}
},
@ -120,13 +126,17 @@ export default {
this.getCategories();
},
methods: {
async openTalk() {
await this.hideProductList('quick')
this.$store.commit('setShowTalk', true);
},
getCategories() {
this.loading = true;
reqNcMatchMenu({ url_link: window.location.href }).then(res => {
this.loading = false;
if (res.status) {
this.categories = buildDynamicStructure(res.data)
console.log("测试",this.categories);
console.log("测试", this.categories);
// this.categories = res.data.map(item => {
// return {
@ -163,7 +173,13 @@ export default {
this.hideTimer = null;
}
},
hideProductList() {
hideProductList(type) {
if (type === 'quick') {
this.currentCategory = null;
this.selectedSecondary = null;
this.hideTimer = null;
return
}
// rightBox
this.hideTimer = setTimeout(() => {
this.currentCategory = null;
@ -258,14 +274,13 @@ export default {
position: absolute;
left: 100%;
top: 0;
border: 1px solid red;
width: 1000px;
background: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 9999;
padding: 25px;
// height: 500px;
min-height: 350px;
margin-left: 10px;
overflow: visible;
white-space: nowrap;
@ -316,20 +331,17 @@ export default {
height: 100%;
max-height: calc(100vh - 280px);
overflow-y: auto;
border: 5px solid red;
.jd-style-menu {
width: 100%;
display: flex;
flex-direction: column;
gap: 20px;
border: 1px solid red;
.category-section {
display: flex;
justify-content: flex-start;
align-items: center;
border: 1px solid blue;
.section-header {
display: flex;
@ -340,8 +352,6 @@ export default {
font-size: 12px;
font-weight: 600;
color: #333;
border: 1px solid red;
;
margin-right: 8px;
}
@ -361,9 +371,7 @@ export default {
align-items: center;
.product-tag {
border: 1px solid red;
height: fit-content !important;
background: #f8f9fa;
font-size: 12px;
color: #666;
cursor: pointer;

View File

@ -89,6 +89,10 @@
</div>
<div class="product-price">
<span class="price">¥{{ item.discount_price }}</span>
<span class="price-unit">{{ item.unit }} {{ item.short_term === '1' ? '(可短租)' : '' }} </span>
</div>
<div style="display: flex;justify-content: space-between;align-items: center;">

View File

@ -114,6 +114,10 @@
<div class="form-row">
<el-form-item label="价格" prop="price" required class="form-item-half">
<template slot="label">
<span v-if="publish_type === '1'" >商品价格</span>
<span v-if="publish_type === '2'">预期价格</span>
</template>
<div class="price-unit-container">
<el-input v-model="form.price" placeholder="请输入价格" class="price-input"></el-input>
<el-input v-model="form.unit" placeholder="单位" class="unit-input"></el-input>
@ -149,9 +153,10 @@
style="margin-left: 5px; color: #333; cursor: pointer;"></i>
</el-tooltip>
</template>
<span v-if="form.discount !== 0 && form.price" style="color: red;font-size: 14px;font-weight: bold;">{{
((form.price *
form.discount/10)*0.03).toFixed(2) }}</span>
<span v-if=" form.price && !isNaN(Number(form.price))" style="color: red;font-size: 14px;font-weight: bold;">
<span v-if="form.discount == 0 || !form.discount">{{ Number(form.price)* 0.03.toFixed(2) }}</span>
<span v-else>{{ (Number(form.price) * Number(form.discount) / 10 * 0.03).toFixed(2) }}</span>
</span>
<span v-else>-</span>
</el-form-item>
@ -594,6 +599,9 @@ export default {
let formdata = new FormData();
for (let key in this.form) {
if(key == 'img'&&this.form.img == null){
continue;
}
formdata.append(key, this.form[key]);
}
formdata.append('publish_type', this.publish_type)

View File

@ -1,12 +1,13 @@
<template>
<div class="enterprise-demand-detail">
<div style="display: flex;justify-content: space-between;align-items: center;">
<div style="display: flex;justify-content: space-between;align-items: center;">
<h1 class="demand-title" style="margin: 0 0;">
{{ productDetailInfo.product_name }}
</h1>
<span style="color: red;font-weight: bold;font-size: 20px;">{{ productDetailInfo.discount_price }}{{ productDetailInfo.unit }}</span>
<button v-if="!isApprove" class="consult-btn" @click="openTalk">立即咨询</button>
</div>
</div>
<!-- 标题区域 -->
<ul class="title-section">
@ -14,6 +15,11 @@
<span class="mylabel">企业名称</span>
<span class="value">{{ productDetailInfo.company_name }}</span>
</li>
<!-- <li class="info-item" style="margin:5px 0;">
<span class="mylabel"> <span v-if="productDetailInfo.publish_type === '1' ">商品价格</span> <span
v-else>预期价格</span></span>
<span class="value">{{ productDetailInfo.discount_price }}</span> -->
<!-- </li> -->
<li class="info-item" style="margin:5px 0;">
<span class="mylabel">所属类别</span>
<span class="value">{{ productDetailInfo.product_category }}</span>
@ -28,7 +34,11 @@
<ul class="infoUl">
<li class="contact-item">
<span class="mylabel">联系人</span>
<span class="value">{{ productDetailInfo.contact_person }} {{ productDetailInfo.phone_number }}</span>
<span class="value">{{ productDetailInfo.contact_person }} </span>
</li>
<li class="contact-item">
<span class="mylabel">手机号码</span>
<span class="value">{{ productDetailInfo.phone_number }}</span>
</li>
<li class="contact-item">
<span class="mylabel">邮箱</span>
@ -43,13 +53,14 @@
<span class="value">{{ productDetailInfo.create_at ? productDetailInfo.create_at : '暂未发布' }}</span>
</li>
</ul>
<div v-if="productDetailInfo.cart_flag==='1'" style="position: relative;width: 100%;padding-left: 16px;" class="mysection">
<div v-if="productDetailInfo.cart_flag === '1'" style="position: relative;width: 100%;padding-left: 16px;"
class="mysection">
<div class="section-title">配置数据</div>
<div class="section-content">
</div>
</div>
<ul v-if="productDetailInfo.cart_flag==='1'" class="adUl">
<ul v-if="productDetailInfo.cart_flag === '1'" class="adUl">
<li>
<div class="leftBox">
<img src="https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssc2x.png" alt="">
@ -165,7 +176,7 @@ export default {
</script>
<style scoped lang="scss">
.consult-btn {
.consult-btn {
width: fit-content;
background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%);
color: white;
@ -181,7 +192,8 @@ export default {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(39, 90, 255, 0.3);
}
}
}
.mylabel {
font-weight: 500;
color: #252424;
@ -344,6 +356,7 @@ export default {
border-radius: 8px;
.info-item {
width: 120px;
display: flex;
margin-bottom: 10px;
@ -433,7 +446,7 @@ export default {
margin-bottom: 30px;
.section-title {
margin-bottom: 10px;
margin-bottom: 10px;
font-size: 14px;
color: #333;
font-weight: bold !important;

View File

@ -5,7 +5,7 @@
<img @click="goHome" class="logo-clickable" style="width: 180px;height: 60px;padding-right: 20px;"
src="https://www.kaiyuancloud.cn/idfile?path=logo_ncmatch.png" alt="">
<div
style="min-width:800px;border-bottom: 1px solid #E5E5E5;display: flex;align-items: center;justify-content: space-between;">
style="min-width:800px;display: flex;align-items: center;justify-content: space-between;">
<!-- 中间搜索区域 -->
<div class="search-section" style="position: relative;margin: 0 20px;">
@ -33,7 +33,7 @@
</div>
<!-- 热搜关键词 -->
<div class="hot-search">
<div v-if="false" class="hot-search">
<a v-for="keyword in hotSearchKeywords" :key="keyword" href="#" class="hot-keyword"
@click="handleHotKeywordClick(keyword)">
@ -44,6 +44,7 @@
</div>
<!-- position: absolute;right: -150px;top: 0px; -->
<span
v-if="false"
style="height: 44px;line-height: 44px;border-radius: 8px;border:1px solid #275AFF; color: #275AFF;font-size: 18px;font-weight: 500;padding: 0 20px;z-index: 10;display: flex;align-items: center;gap: 10px;">
<img style="width: 24px;height: 24px;" src="../mainPage/img/robot.svg" alt="">
NC AI</span>
@ -561,7 +562,7 @@ export default {
.search-section {
flex: 1;
min-width: 850px;
padding-bottom: 10px;
// padding-bottom: 10px;
.search-bar {
display: flex;
align-items: center;

View File

@ -855,11 +855,12 @@ export default {
} else if (this.$route.query.type === 'rqy') {
this.$router.push("/product/productHome/k8s/createK8s");
} else {
this.$router.push("/homePage/index");
this.$router.push(getHomePath());
}
} else {
this.$router.push({name: "productHome"});
this.$router.push(getHomePath());
}
} else if (res.roles.includes('运营')) {

View File

@ -0,0 +1,60 @@
<template>
<div>
<el-button size="small" style="margin-bottom: 5px;" type="primary" @click="getTableData('1')">
<i class="el-icon-upload2"> </i>
全部导出</el-button>
<el-table border :data="tableData" style="width: 100%">
<el-table-column prop="name" label="姓名" min-width="180">
</el-table-column>
<el-table-column prop="phone" label="手机号" min-width="180">
</el-table-column>
<el-table-column prop="email" label="邮箱" min-width="180">
</el-table-column>
<el-table-column prop="content" label="内容" min-width="180">
</el-table-column>
<el-table-column prop="create_time" label="创建时间" min-width="180">
</el-table-column>
<el-table-column prop="update_time" label="更新时间" min-width="180">
</el-table-column>
</el-table>
</div>
</template>
<script>
import { reqSearchUserInquiry } from '@/api/ncmatch'
import XLSX from 'xlsx'
export default {
name: 'ConsultingMangement',
data() {
return {
tableData: []
}
},
created() {
this.getTableData('0')
},
methods: {
getTableData(to_excel) {
reqSearchUserInquiry({ url_link: window.location.href, to_excel: to_excel }).then(res => {
console.log(res)
if (res.status) {
if (to_excel === '0') {
this.tableData = res.data
} else if (to_excel === '1') {
// Workbook
const wb = XLSX.utils.book_new();
// sheet
const ws1 = XLSX.utils.json_to_sheet(res.data);
XLSX.utils.book_append_sheet(wb, ws1, 'sheet1');
// Excel
XLSX.writeFile(wb, '咨询列表.xlsx');
this.$message.success('导出成功')
}
}
})
}
}
}
</script>
<style scoped></style>

View File

@ -36,18 +36,75 @@
<el-drawer title="菜单管理" :visible.sync="drawer" :direction="direction">
<div style="margin: 20px;"></div>
<el-form :label-position="labelPosition" label-width="80px" :model="formLabelAlign">
<el-form-item label="菜单名称">
<el-input v-model="formLabelAlign.name"></el-input>
<el-form style="margin: 10px;" :label-position="labelPosition" label-width="100px" :model="formLabelAlign" :rules="formRules" ref="menuForm">
<el-form-item label="菜单名称" prop="name">
<el-input v-model="formLabelAlign.name" placeholder="请输入菜单名称"></el-input>
</el-form-item>
<el-form-item v-if="!formLabelAlign.parentid" label="菜单图标" prop="icon">
<div class="icon-upload-container">
<!-- 图片预览区域 -->
<div v-if="formLabelAlign.icon" class="icon-preview">
<div class="preview-wrapper">
<!-- SVG图片显示 -->
<img v-if="isSvgImage(formLabelAlign.icon)"
:src="formLabelAlign.icon"
class="preview-image svg-image"
alt="菜单图标">
<!-- 普通图片显示 -->
<img v-else
:src="formLabelAlign.icon"
class="preview-image"
alt="菜单图标">
<div class="preview-actions">
<el-button type="text" size="mini" @click="removeIcon" icon="el-icon-delete" style="color: #F56C6C;">删除</el-button>
</div>
</div>
</div>
<!-- 上传组件 -->
<el-upload
v-if="!formLabelAlign.icon"
class="icon-uploader"
action="#"
:http-request="handleIconUpload"
:show-file-list="false"
:before-upload="beforeIconUpload"
accept=".svg,.png,.jpg"
drag>
<i class="el-icon-plus uploader-icon"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<!-- <div class="el-upload__tip" slot="tip">
<i class="el-icon-info"></i>
支持 SVGPNGJPG格式文件大小不超过 5MB
</div> -->
</el-upload>
</div>
</el-form-item>
<el-form-item label="菜单排序" style="margin-top: 10px!important;" prop="poriority">
<el-input-number :controls="false" v-model="formLabelAlign.poriority" :step="1" :min="0" :max="100" placeholder="请输入排序值"></el-input-number>
</el-form-item>
<el-form-item label="菜单来源" prop="source">
<el-input v-model="formLabelAlign.source" placeholder="请输入菜单来源"></el-input>
</el-form-item>
<!-- 操作按钮 -->
<el-form-item>
<el-button type="primary" @click="submitForm">保存</el-button>
<el-button @click="resetForm">重置</el-button>
<el-button @click="drawer = false">取消</el-button>
</el-form-item>
</el-form>
</el-drawer>
</div>
</template>
<script>
import { reqNcMatchMenu } from '@/api/ncmatch';
import { reqNcMatchMenu,reqAddProductMenu } from '@/api/ncmatch';
export default {
name: 'menuMangement',
data() {
@ -58,10 +115,23 @@ export default {
drawer: false,
direction: 'rtl',
labelPosition: 'right',
formLabelAlign: {
name: '',
region: '',
type: ''
icon: '',
iconFile: null, //
url_link: '',
parentid: '',
poriority: '',
source: '',
},
formRules: {
name: [
{ required: true, message: '请输入菜单名称', trigger: 'blur' }
],
poriority: [
{ required: true, message: '请输入菜单排序', trigger: 'blur' }
]
}
}
},
@ -69,8 +139,155 @@ export default {
this.getCategories();
},
methods: {
//
handleIconUpload(options) {
const file = options.file;
console.log('上传文件:', file);
// URL
const reader = new FileReader();
reader.onload = (e) => {
this.formLabelAlign.icon = e.target.result;
this.$message.success('图片上传成功!');
};
reader.readAsDataURL(file);
//
this.formLabelAlign.iconFile = file;
},
//
beforeIconUpload(file) {
//
const isValidType = this.isValidImageType(file.type);
if (!isValidType) {
this.$message.error('只支持 SVG、PNG、JPG格式的图片');
return false;
}
// (5MB)
const isLt5M = file.size / 1024 / 1024 < 5;
if (!isLt5M) {
this.$message.error('图片大小不能超过 5MB');
return false;
}
return true;
},
//
isValidImageType(type) {
const validTypes = [
'image/svg+xml',
'image/png',
'image/jpeg',
'image/jpg',
'image/gif',
'image/webp'
];
return validTypes.includes(type);
},
// SVG
isSvgImage(src) {
return src && (src.includes('.svg') || src.startsWith('data:image/svg+xml'));
},
//
removeIcon() {
this.$confirm('确定要删除这个图标吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.formLabelAlign.icon = '';
this.formLabelAlign.iconFile = null;
this.$message.success('图标已删除');
}).catch(() => {});
},
//
submitForm() {
this.$refs.menuForm.validate((valid) => {
if (valid) {
// FormData
const formData = new FormData();
//
formData.append('name', this.formLabelAlign.name);
formData.append('poriority', this.formLabelAlign.poriority);
formData.append('source', this.formLabelAlign.source);
formData.append('url_link', window.location.href);
// parentidnull
if (!this.formLabelAlign.parentid || this.formLabelAlign.parentid === '' || this.formLabelAlign.parentid === '0') {
if (this.formLabelAlign.iconFile) {
formData.append('icon', this.formLabelAlign.iconFile);
console.log('包含图片上传');
} else if (this.formLabelAlign.icon) {
// URL
formData.append('iconUrl', this.formLabelAlign.icon);
console.log('包含现有图片URL');
}
} else {
console.log('跳过图片上传parentid不为null');
}
// parentid
if (this.formLabelAlign.parentid) {
formData.append('parentid', this.formLabelAlign.parentid);
}
// FormData
for (let [key, value] of formData.entries()) {
console.log(`${key}:`, value);
}
// API
this.saveMenuData(formData);
} else {
this.$message.error('请检查表单信息!');
return false;
}
});
},
//
saveMenuData(formData) {
// API
//
reqAddProductMenu(formData).then(res => {
if (res.status) {
this.$message.success('保存成功!');
this.drawer = false;
this.getCategories(); //
}else{
this.$message.error(res.msg);
}
}).catch(error => {
this.$message.error('保存失败:' + error.message);
});
// //
// this.$message.success('');
// this.drawer = false;
// console.log('FormData');
},
//
resetForm() {
this.$refs.menuForm.resetFields();
this.formLabelAlign.icon = '';
this.formLabelAlign.iconFile = null;
},
addNode(row, type) {
this.drawer = true;
this.resetForm();
if (type === 'child') {
this.formLabelAlign.parentid = row.id;
} else if (type === 'sibling') {
this.formLabelAlign.parentid = row.parentid;
}
console.log('新增节点:', row, '类型:', type);
},
getCategories() {
@ -89,11 +306,20 @@ export default {
},
editNode(row) {
this.drawer = true;
this.formLabelAlign = { ...row };
// iconFile
this.formLabelAlign.iconFile = null;
console.log('编辑节点:', row);
},
deleteNode(row) {
this.drawer = true;
this.$confirm('确定要删除这个菜单吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
console.log('删除节点:', row);
this.$message.success('删除成功!');
}).catch(() => {});
},
buildTree(data, parentId = null) {
if (!Array.isArray(data)) {
@ -323,4 +549,172 @@ export default {
.el-loading-mask {
background-color: rgba(255, 255, 255, 0.8);
}
.license-uploader {
border: 2px dashed #d1d9e0;
border-radius: 12px;
cursor: pointer;
position: relative;
overflow: hidden;
width:150px;
height: 150px;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&:hover {
border-color: #667eea;
background: linear-gradient(135deg, #f0f4ff 0%, #e8f0ff 100%);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.2);
}
.license-uploader-icon {
font-size: 28px;
color: #667eea;
width: 100%;
height: 160px;
line-height: 160px;
text-align: center;
transition: all 0.3s ease;
}
.license-image {
width: 100%;
height: 160px;
display: block;
object-fit: cover;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
}
/* 图标上传容器样式 */
.icon-upload-container {
width: 100%;
}
/* 图标预览样式 */
.icon-preview {
margin-bottom: 15px;
}
.preview-wrapper {
position: relative;
display: inline-block;
border: 2px dashed #d9d9d9;
border-radius: 8px;
padding: 10px;
background: #fafafa;
transition: all 0.3s;
}
.preview-wrapper:hover {
border-color: #409eff;
background: #f0f9ff;
}
.preview-image {
max-width: 120px;
max-height: 120px;
object-fit: contain;
border-radius: 4px;
display: block;
}
.svg-image {
background: linear-gradient(45deg, #f0f0f0 25%, transparent 25%),
linear-gradient(-45deg, #f0f0f0 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #f0f0f0 75%),
linear-gradient(-45deg, transparent 75%, #f0f0f0 75%);
background-size: 20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
}
.preview-actions {
margin-top: 10px;
text-align: center;
}
.preview-actions .el-button {
margin: 0 5px;
}
/* 上传组件样式 */
.icon-uploader {
width: 150px;
height: 150px;
}
.icon-uploader .el-upload {
width: 100%;
}
::v-deep .icon-uploader .el-upload-dragger {
width: 150px!important;
height: 150px!important;
border: 2px dashed #d9d9d9;
border-radius: 8px;
background: #fafafa;
transition: all 0.3s;
display: flex ;
flex-direction: column;
align-items: center;
justify-content:center;
}
.icon-uploader .el-upload-dragger:hover {
border-color: #409eff;
background: #f0f9ff;
}
.uploader-icon {
font-size: 28px;
color: #8c939d;
margin-bottom: 10px;
}
.el-upload__text {
color: #606266;
font-size: 14px;
margin-bottom: 5px;
}
.el-upload__text em {
color: #409eff;
font-style: normal;
}
.el-upload__tip {
font-size: 12px;
color: #909399;
margin-top: 10px;
}
/* 表单样式优化 */
.el-form-item {
margin-bottom: 20px;
}
.el-form-item__label {
font-weight: 500;
color: #606266;
}
/* 响应式设计 */
@media (max-width: 768px) {
.preview-image {
max-width: 80px;
max-height: 80px;
}
.icon-uploader .el-upload-dragger {
height: 100px;
}
}
</style>

View File

@ -1,6 +1,6 @@
export function getHomePath() {
let homePath= "/homePage/index"
// let homePath= "/ncmatchHome/index"
// let homePath= "/homePage/index"
let homePath= ""
// 业主机构信息
let url_link = window.location.href || '';
@ -18,11 +18,14 @@ export function getHomePath() {
!url_link.includes('/domain/')) {
if (domain_url.includes('ncmatch') || domain_url.includes('9527')) {
homePath = '/homePath/index'
} else if (domain_url.includes('kaiyuancloud') || domain_url.includes('opencomputing') || domain_url.includes('localhost')) {
homePath = '/ncmatchHome/index'
} else if (domain_url.includes('kaiyuancloud') || domain_url.includes('opencomputing') || domain_url.includes('localhost')) {
homePath = '/homePage/index'
}
}else{
homePath = '/homePage/index'
}
console.log("res是",homePath)
// this.$message.success("homePath是"+homePath)
console.log("homePath是",homePath)
return homePath
}