main #7

Merged
charles merged 7 commits from main into prod 2025-08-14 17:16:50 +08:00
43 changed files with 4062 additions and 589 deletions

View File

@ -0,0 +1,753 @@
async def user_action_record(ns={}):
ns_dic = {
'id': uuid(),
'source': '百度智能云',
'orderid': ns.get('orderid'),
'ordertype': ns.get('ordertype'),
'userid': ns.get('userid'),
'reason': ns.get('reason')
}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
await sor.C('user_action', ns_dic)
async def time_convert(resoucetime=None):
if not resoucetime:
return
utc_time = datetime.datetime.strptime(resoucetime, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=datetime.timezone.utc)
beijing_time = utc_time.astimezone(datetime.timezone(datetime.timedelta(hours=8)))
return beijing_time.strftime("%Y-%m-%d %H:%M:%S")
async def cal_expire_time(history_time=None, chargeduration=None, unit=None):
chargeduration = int(chargeduration)
# 当前时间
# now = datetime.datetime.now()
now = datetime.datetime.strptime(history_time, '%Y-%m-%d %H:%M:%S')
if unit == 'MONTH':
expire_time = now + dateutil.relativedelta.relativedelta(months=chargeduration)
elif unit == 'YEAR':
expire_time = now + dateutil.relativedelta.relativedelta(years=chargeduration)
else:
expire_time = None
if expire_time:
return str(expire_time)
else:
return None
async def affirmbz_order(ns={}):
"""确认支付"""
order_type = ns.get('order_type')
sor = ns['sor']
orgid = await sor.R('bz_order', {'id': ns['orderid']})
servicename = orgid[0]['servicename']
product_url = None
# if ('BCC' in servicename) or ('GPU' in servicename):
# product_url = 'https://console.vcp.baidu.com/bcc/#/bcc/instance/list'
date = await get_business_date(sor=None)
# await sor.U('bz_order',{'id':ns['orderid'],'order_date': date})
count = await getCustomerBalance(sor, orgid[0]['customerid'])
if count == None:
count = 0
if count - float(orgid[0]['amount']) < 0:
pricedifference = count - round(orgid[0]['amount'],2)
return {'status': False, 'msg': '账户余额不足','pricedifference': round(pricedifference,2)}
await order2bill(ns['orderid'], sor)
bills = await sor.R('bill', {'orderid': ns['orderid'], 'del_flg': '0'})
try:
# 需要加事务
for i in bills:
ba = BillAccounting(i)
r = await ba.accounting(sor)
dates = datetime.datetime.now()
await sor.U('bz_order', {'id': ns['orderid'], 'order_status': '1','create_at':dates})
await sor.U('bill', {'id': ns['orderid'], 'bill_state': '1'})
order_goods = await sor.R('order_goods', {'orderid': ns['orderid']})
for j in order_goods:
# 处理退订逻辑
if order_type == 'REFUND':
# 找到资源并更新时间
resource_find_sql = """select id from customer_goods where resourceid = '%s';""" % j['resourceids']
resource_find_li = await sor.sqlExe(resource_find_sql, {})
resource_find_id = resource_find_li[0]['id']
await sor.U('customer_goods', {'id': resource_find_id, 'del_flg': '1'})
# 处理续费逻辑
elif order_type == 'RENEW':
# 找到资源并更新时间
resource_find_sql = """select id from customer_goods where FIND_IN_SET('%s', resourceid) and del_flg = '0';""" % j['resourceids']
resource_find_li = await sor.sqlExe(resource_find_sql, {})
resource_find_id = resource_find_li[0]['id']
await sor.U('customer_goods', {'id': resource_find_id, 'start_date': j['resourcestarttime'], 'expire_date': j['resourceendtime']})
# 处理购买逻辑
else:
product = await sor.R('product', {'id': j['productid']})
nss = {}
nss['id'] = uuid()
# nss['id'] = UUID()
nss['providerrid'] = product[0]['providerid']
nss['productname'] = product[0]['name']
nss['productdesc'] = product[0]['description']
nss['customerid'] = orgid[0]['customerid']
nss['productid'] = product[0]['id']
nss['specdataid'] = j['spec_id']
nss['orderid'] = orgid[0]['id']
nss['start_date'] = j['resourcestarttime']
nss['expire_date'] = j['resourceendtime']
nss['resourceid'] = j['resourceids']
nss['orderkey'] = j['orderkey']
if product_url:
nss['product_url'] = product_url
else:
spec = json.loads(product[0]['spec_note']) if isinstance(product[0]['spec_note'], str) else product[0]['spec_note']
spec_list_url = [item['value'] for item in spec if item['configName'] == 'listUrl']
nss['product_url'] = spec_list_url[0] if spec_list_url else 'https://console.vcp.baidu.com/bcc/#/bcc/instance/list'
await sor.C('customer_goods', nss)
return {'status': True, 'msg': '支付成功'}
except Exception as error:
return {'status': False, 'msg': str(error)}
async def baidu_new_update_resouce(ns={}):
# 增加延迟
import asyncio
await asyncio.sleep(12)
db = DBPools()
async with db.sqlorContext('kboss') as sor:
baidu_users = await sor.R('baidu_users', {'user_id': ns.get('userid'),'del_flg':'0'})
user = await sor.R('users', {'id': ns.get('userid')})
orgid = await sor.R('organization', {'id': user[0]['orgid']})
nss = {'uuids': [ns.get('order_id')], 'queryAccountId': baidu_users[0]['baidu_id']}
ns_format = '&'.join(['%s=%s' % (k, v) for k, v in ns.items()])
url = 'https://billing.baidubce.com/v1/order/getByUuid?%s' % ns_format
method = 'POST'
header = {
"Host": "billing.baidubce.com",
"ContentType": "application/json;charset=UTF-8"
}
header = await get_auth_header(method=method, url=url, header=header)
async with aiohttp_client.request(
method=method,
url=url,
headers=header,
json=nss) as res:
data_ = await res.json()
with open('baidu_new_order_after_confirm.txt', 'a+') as f:
f.write(json.dumps(data_) + '\n')
orders = data_['orders']
for item in orders:
order_items = item['orderItems']
for order_info in order_items:
resourceids = ','.join(order_info['shortIds']) if order_info.get('shortIds') else ''
if not resourceids:
continue
resourcestarttime = await time_convert(order_info.get('resourceStartTime')) if order_info.get(
'resourceStartTime') else None
resourceendtime = await time_convert(order_info.get('resourceEndTime')) if order_info.get(
'resourceEndTime') else None
order_key = order_info['key']
update_order_goods_sql = """ UPDATE order_goods og
JOIN bz_order o ON og.orderid = o.id
SET
og.resourceids = '%s',
og.resourcestarttime = '%s',
og.resourceendtime = '%s'
WHERE
og.orderkey = '%s'
AND o.provider_orderid = '%s'; """ \
% (resourceids, resourcestarttime, resourceendtime, order_key, ns.get('order_id'))
await sor.sqlExe(update_order_goods_sql, {})
update_customer_goods_sql = """ UPDATE customer_goods og
JOIN bz_order o ON og.orderid = o.id
SET
og.resourceid = '%s',
og.start_date = '%s',
og.expire_date = '%s'
WHERE
og.orderkey = '%s'
AND o.provider_orderid = '%s'; """ \
% (resourceids, resourcestarttime, resourceendtime, order_key, ns.get('order_id'))
await sor.sqlExe(update_customer_goods_sql, {})
async def baidu_order_cancel(ns={}):
baidu_id = ns['baidu_id']
order_id = ns['order_id']
paydata = {'queryAccountId': baidu_id, 'orderIds': [order_id]}
ns_format = '&'.join(['%s=%s' % (k, v) for k, v in ns.items()])
url = 'https://billing.baidubce.com/v1/order/cancel?%s' % ns_format
method = 'POST'
header = {
"Host": "billing.baidubce.com"
}
header = await get_auth_header(method=method, url=url, header=header)
async with aiohttp_client.request(
method=method,
url=url,
headers=header,
json=paydata) as res:
await res.json()
return {
'status': True,
'msg': 'order cancel success'
}
async def get_baidu_orderlist(ns={}):
"""
百度支付
1、获取订单
2、算出购买的产品折扣
3、比对账号余额
"""
# 增加延迟
import asyncio
await asyncio.sleep(2)
db = DBPools()
async with db.sqlorContext('kboss') as sor:
baidu_users = await sor.R('baidu_users', {'user_id': ns.get('userid'),'del_flg':'0'})
user = await sor.R('users', {'id': ns.get('userid')})
orgid = await sor.R('organization', {'id': user[0]['orgid']})
nss = {'uuids': [ns.get('order_id')], 'queryAccountId': baidu_users[0]['baidu_id']}
ns_format = '&'.join(['%s=%s' % (k, v) for k, v in ns.items()])
url = 'https://billing.baidubce.com/v1/order/getByUuid?%s' % ns_format
method = 'POST'
header = {
"Host": "billing.baidubce.com",
"ContentType": "application/json;charset=UTF-8"
}
header = await get_auth_header(method=method, url=url, header=header)
async with aiohttp_client.request(
method=method,
url=url,
headers=header,
json=nss) as res:
data_ = await res.json()
with open('baidu_new_order.txt', 'a+') as f:
f.write(json.dumps(data_) + '\n')
orders = data_['orders']
serviceType = orders[0]['orderItems']
# 可能获取得到的是延迟订单
if orders[0]['type'] == 'REFUND' and orders[0]['status'] != 'CREATED':
return {
'status': False,
'msg': 'delay_order'
}
# 避免重复退订
if orders[0]['type'] == 'REFUND' and orders[0]['status'] == 'CREATED':
order_history = await sor.R('bz_order', {'provider_orderid': ns.get('order_id'), 'business_op': 'BUY_REVERSE', 'order_status': '1'})
if order_history:
print('此订单之前已经退费成功')
return {
'status': True,
'msg': '此订单之前已经退费成功'
}
# 如果是退款 更新数据库状态
if orders[0]['type'] == 'REFUND':
updatetime = await time_convert(orders[0]['updateTime']) if orders[0].get('updateTime') else None
update_refund_sql = """UPDATE baidu_orders SET price = '%s', status = '%s', updatetime = '%s' WHERE orderid = '%s';""" % \
(float(orders[0]['price']), orders[0]['status'], updatetime, ns.get('order_id'))
await sor.sqlExe(update_refund_sql, {})
productType = 'prepay'
# 判断订单item中productType是否有后付费的产品
for item in orders:
order_items = item['orderItems']
for order_info in order_items:
postpay_price = order_info['itemFee']['price'] if order_info.get('itemFee') else order_info['catalogPrice']
if not postpay_price:
# cpt1Price: 固定配置,按分钟计费
postpay_price = order_info['pricingDetail'].get('cpt1Price') if order_info.get('pricingDetail') else 0
# 确定是否是后付费订单
if order_info['productType'] == 'postpay' and postpay_price != 0:
productType = 'postpay'
# 获取余额
user_balance = await getCustomerBalance(sor, orgid[0]['id'])
# 判断余额是否大于50
if user_balance < 50:
await sor.rollback()
paydata = {'queryAccountId': baidu_users[0]['baidu_id'], 'orderIds': [ns.get('order_id')]}
ns_format = '&'.join(['%s=%s' % (k, v) for k, v in ns.items()])
url = 'https://billing.baidubce.com/v1/order/cancel?%s' % ns_format
method = 'POST'
header = {
"Host": "billing.baidubce.com"
}
header = await get_auth_header(method=method, url=url, header=header)
async with aiohttp_client.request(
method=method,
url=url,
headers=header,
json=paydata) as res:
await res.json()
ns_record = {
'orderid': ns.get('order_id'),
'ordertype': orders[0]['type'],
'userid': ns.get('userid'),
'reason': '后付费 该账号余额不足50无法完成购买'
}
await user_action_record(ns_record)
return {
'status': False,
'msg': '您的余额小于该产品的起购金额50元, 目前无法购买立即充值'
}
# 实付价格
total_price = 0
# productType = ''
# 买/续/退 字段映射
order_type = orders[0]['type']
if order_type == 'NEW':
business_op = 'BUY'
elif order_type == 'RENEW':
business_op = 'RENEW'
elif order_type == 'REFUND':
business_op = 'BUY_REVERSE'
else:
ns_record = {
'orderid': ns.get('order_id'),
'ordertype': orders[0]['type'],
'userid': ns.get('userid'),
'reason': '支付形式目前仅包含购买,续费,退订'
}
await user_action_record(ns_record)
return {
'status': False,
'msg': '线上暂不支持, 请联系售后'
}
try:
# 生成本地订单
bz_ns = {}
bz_ns['id'] = uuid()
bz_ns['order_status'] = '0'
bz_ns['business_op'] = business_op
bz_ns['userid'] = ns.get('userid')
bz_ns['customerid'] = orgid[0]['id']
bz_ns['order_date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
bz_ns['thirdparty_order'] = ns.get('order_id')
bz_ns['source'] = '百度智能云'
# bz_ns['originalprice'] = orders[0]['price']
bz_ns['originalprice'] = sum(i['catalogPrice'] for i in serviceType)
bz_ns['provider_orderid'] = ns.get('order_id')
bz_ns['ordertype'] = orders[0]['productType']
bz_ns['servicename'] = orders[0]['serviceType']
bz_ns['autoreneworder'] = '1' if orders[0]['autoRenewOrder'] else '0'
if ns.get('specdataid'):
bz_ns['specdataid'] = ns['specdataid']
await sor.C('bz_order', bz_ns)
for i in serviceType:
# if i['productType'] == 'prepay':
# # 预付费
# productType = 'prepay'
# financePrice = 0
# 获取产品id
product = await sor.R('product', {'providerpid': 'baidu_' + i['serviceType'], 'del_flg': '0'})
if not product:
return {
'status': False,
'msg': '未配置该产品, 请联系售后处理'
}
# 获取协议
saleprotocol_to_person = await sor.R('saleprotocol',
{'bid_orgid': orgid[0]['id'], 'offer_orgid': orgid[0]['parentid'],
'del_flg': '0'})
# 等于空就代表这个客户没有特殊折扣,就要找到买方为*的协议
saleprotocol_to_all = await sor.R('saleprotocol', {'bid_orgid': '*', 'offer_orgid': orgid[0]['parentid'],
'del_flg': '0', 'salemode': '0'})
if saleprotocol_to_person:
product_salemode = await sor.R('product_salemode',
{'protocolid': saleprotocol_to_person[0]['id'],
'productid': product[0]['id'],
'del_flg': '0'})
if not product_salemode:
product_salemode = await sor.R('product_salemode',
{'protocolid': saleprotocol_to_all[0]['id'],
'productid': product[0]['id'],
'del_flg': '0'})
else:
product_salemode = await sor.R('product_salemode',
{'protocolid': saleprotocol_to_all[0]['id'],
'productid': product[0]['id'],
'del_flg': '0'})
if not product_salemode:
return {
'status': False,
'msg': '还未上线这个产品的协议配置'
}
supply_price = i['itemFee']['price'] if i.get('itemFee') else i['catalogPrice']
financePrice = abs(supply_price * product_salemode[0]['discount'])
total_price += financePrice
# 添加订单子表
nss = {}
nss['id'] = uuid()
nss['orderid'] = bz_ns['id']
nss['productid'] = product[0]['id']
nss['providerid'] = product[0]['providerid']
if i['count'] > 1:
nss['list_price'] = abs(i['realCatalogPrice'] / i['count'])
else:
nss['list_price'] = abs(i['realCatalogPrice'])
nss['discount'] = product_salemode[0]['discount']
nss['quantity'] = i['count']
nss['price'] = abs(round(nss['list_price'] * product_salemode[0]['discount'], 2))
# 计算总价
# nss['amount'] = round(total_price, 2)
nss['amount'] = abs(round(nss['price'] * nss['quantity'], 2))
nss['chargemode'] = i.get('productType')
nss['servicename'] = i.get('serviceType')
nss['chargeduration'] = i.get('time')
nss['unit'] = i.get('timeUnit')
nss['resourceids'] = ','.join(i['shortIds']) if i.get('shortIds') else ''
nss['orderkey'] = i.get('key')
# 如果是续费订单 由于没有返回日期, 重新计算日期
if order_type == 'RENEW':
history_time_sql = "select resourcestarttime, resourceendtime from order_goods where FIND_IN_SET('%s', resourceids) order by resourceendtime desc;" % \
nss['resourceids']
history_time = await sor.sqlExe(history_time_sql, {})
new_end_time = await cal_expire_time(history_time=history_time[0]['resourceendtime'],
chargeduration=nss['chargeduration'], unit=nss['unit'])
# 开始日期不变 更新到期日期
nss['resourcestarttime'] = history_time[0]['resourcestarttime']
nss['resourceendtime'] = new_end_time
else:
if i.get('resourceStartTime'):
nss['resourcestarttime'] = await time_convert(i.get('resourceStartTime'))
else:
nss['resourcestarttime'] = await time_convert(orders[0]['updateTime'])
if i.get('resourceEndTime'):
nss['resourceendtime'] = await time_convert(i.get('resourceEndTime'))
# 后付费没有资源结束时间
if i.get('productType') == 'prepay':
end_time = await time_convert(orders[0]['updateTime'])
nss['resourceendtime'] = await cal_expire_time(history_time=end_time,
chargeduration=nss['chargeduration'],
unit=nss['unit'])
else:
nss['resourceendtime'] = None
await sor.C('order_goods', nss)
# 循环后更新订单中总价
await sor.U('bz_order', {'id': bz_ns['id'], 'amount': round(total_price, 2)})
except Exception as e:
await baidu_order_cancel({'baidu_id': baidu_users[0]['baidu_id'], 'order_id': ns.get('order_id')})
ns_record = {
'orderid': ns.get('order_id'),
'ordertype': orders[0]['type'],
'userid': ns.get('userid'),
'reason': '发生错误, %s' % str(e)[:100]
}
await user_action_record(ns_record)
import traceback
with open('baiducloud_err.txt', 'w') as f:
f.write(str(e)+ traceback.format_exc())
traceback.print_exc()
await sor.rollback()
return {
'status': False,
'msg': '产品错误, 请联系售后'
}
# 判断用户账户余额是否足够支付
try:
count = await getCustomerBalance(sor, orgid[0]['id'])
if order_type == 'REFUND':
count = total_price + 0.1
if round(total_price,2) <= count:
#判断预付费或者后付费
if productType == 'prepay' or productType == 'postpay':
# 调用扣费接口
affirmbz_order_ns = {
'sor': sor,
'orderid': bz_ns['id'],
'order_type': order_type
}
affirmbz_order_res = await affirmbz_order(affirmbz_order_ns)
if not affirmbz_order_res['status']:
# if True:
await sor.rollback()
ns_record = {
'orderid': ns.get('order_id'),
'ordertype': orders[0]['type'],
'userid': ns.get('userid'),
'reason': '支付错误, 请联系售后, %s' % affirmbz_order_res.get('msg')
}
await user_action_record(ns_record)
return {
'status': False,
'msg': '支付错误, 请联系售后'
}
# 预配置local_refund用于本地操作
if order_type == 'REFUND' and not ns.get('local_refund'):
return {
'status': True,
'msg': '本地退费成功'
}
else:
# 调用支付订单接口
paydata = {'queryAccountId':baidu_users[0]['baidu_id'],'orderId':ns.get('order_id')}
ns_format = '&'.join(['%s=%s' % (k, v) for k, v in ns.items()])
url = 'https://billing.baidubce.com/v1/order/pay?%s' % ns_format
method = 'POST'
header = {
"Host": "billing.baidubce.com"
}
header = await get_auth_header(method=method, url=url, header=header)
async with aiohttp_client.request(
method=method,
url=url,
headers=header,
json=paydata) as res:
data_ = await res.json()
if data_ == {'success': True}:
ns_record = {
'orderid': ns.get('order_id'),
'ordertype': orders[0]['type'],
'userid': ns.get('userid'),
'reason': '购买成功'
}
await user_action_record(ns_record)
ns_cron_job = {
'id': uuid(),
'source': 'baidu',
'orderid': ns.get('order_id'),
'ordertype': orders[0]['type'],
'userid': ns.get('userid'),
'reason': 'buy success'
}
await sor.C('baidu_cron_job', ns_cron_job)
# return {
# 'status': True,
# 'orderid': bz_ns['id']
# }
else:
await sor.rollback()
await baidu_order_cancel(
{'baidu_id': baidu_users[0]['baidu_id'], 'order_id': ns.get('order_id')})
ns_record = {
'orderid': ns.get('order_id'),
'ordertype': orders[0]['type'],
'userid': ns.get('userid'),
'reason': '支付成功后, order_pay接口错误, 回滚, %s' % str(data_)[:400]
}
await user_action_record(ns_record)
return {
'status': False,
'msg': '产品分配排队中, 请联系售后详询'
}
else:
# 取消订单
await sor.rollback()
paydata = {'queryAccountId': baidu_users[0]['baidu_id'], 'orderIds': [ns.get('order_id')]}
ns_format = '&'.join(['%s=%s' % (k, v) for k, v in ns.items()])
url = 'https://billing.baidubce.com/v1/order/cancel?%s' % ns_format
method = 'POST'
header = {
"Host": "billing.baidubce.com"
}
header = await get_auth_header(method=method, url=url, header=header)
async with aiohttp_client.request(
method=method,
url=url,
headers=header,
json=paydata) as res:
await res.json()
ns_record = {
'orderid': ns.get('order_id'),
'ordertype': orders[0]['type'],
'userid': ns.get('userid'),
'reason': '无法购买后付费产品'
}
await user_action_record(ns_record)
return {'status': False, 'msg': '无法购买后付费产品'}
else:
#取消订单
await sor.rollback()
paydata = {'queryAccountId':baidu_users[0]['baidu_id'],'orderIds':[ns.get('order_id')]}
ns_format = '&'.join(['%s=%s' % (k, v) for k, v in ns.items()])
url = 'https://billing.baidubce.com/v1/order/cancel?%s' % ns_format
method = 'POST'
header = {
"Host": "billing.baidubce.com"
}
header = await get_auth_header(method=method, url=url, header=header)
async with aiohttp_client.request(
method=method,
url=url,
headers=header,
json=paydata) as res:
await res.json()
ns_record = {
'orderid': ns.get('order_id'),
'ordertype': orders[0]['type'],
'userid': ns.get('userid'),
'reason': '该账号余额不足,无法完成购买'
}
await user_action_record(ns_record)
return {'status': False,'msg': '该账号余额不足,无法完成购买'}
except Exception as e:
await baidu_order_cancel({'baidu_id': baidu_users[0]['baidu_id'], 'order_id': ns.get('order_id')})
import traceback
with open('baiducloud_err.txt', 'w') as f:
f.write(str(e)+ traceback.format_exc())
traceback.print_exc()
await sor.rollback()
return {
'status': False,
'msg': '产品错误, 请联系售后'
}
# 更新资源时间 资源id
# if order_type == 'NEW':
# await baidu_new_update_resouce(ns)
return {
'status': True,
'orderid': bz_ns['id'],
'originalprice': bz_ns.get('originalprice'),
'servicename': bz_ns.get('servicename'),
'amount': total_price
}
async def get_order_list_base_page(baidu_id, pageNo=1, pageSize=500):
ns = {'queryAccountId': baidu_id, 'pageNo': pageNo, 'pageSize': pageSize}
method = 'POST'
ns_format = '&'.join(['%s=%s' % (k, v) for k, v in ns.items()])
url = 'https://billing.baidubce.com/v1/order/list?%s' % ns_format
header = {
"Host": "billing.baidubce.com",
"ContentType": "application/json;charset=UTF-8"
}
header = await get_auth_header(method=method, url=url, header=header)
async with aiohttp_client.request(
method=method,
url=url,
headers=header,
json=ns) as res:
data_orders = await res.json()
return data_orders
async def update_baidu_order_list(ns={}):
"""
ns = {'queryAccountId': '139fc7a23b314596ad78b6bb8e7c1503', 'orderType': 'REFUND'}
:return:
"""
db = DBPools()
async with db.sqlorContext('kboss') as sor:
username = None
# 更新机构下全部用户订单信息
if ns.get('orgid'):
users_find_sql = """SELECT DISTINCT b.baidu_id FROM organization o INNER JOIN users u ON o.id = u.orgid INNER JOIN baidu_users b ON u.id = b.user_id WHERE o.parentid = '%s' AND b.del_flg = '0';""" % ns.get('orgid')
users = await sor.sqlExe(users_find_sql, {})
# 更新个人订单信息
elif ns.get('userid'):
users = await sor.R('baidu_users', {'user_id': ns['userid']})
username_li = await sor.R('users', {'id': ns['userid']})
username = username_li[0]['username'] if username_li else None
else:
users = []
update_count = 0
add_count = 0
for baidu_id in users:
data_orders = await get_order_list_base_page(baidu_id['baidu_id'], pageNo=1, pageSize=1000)
page_num_count = int(data_orders['totalCount'] / data_orders['pageSize']) + 1
for page_num in range(1, page_num_count + 1):
data_orders = await get_order_list_base_page(baidu_id['baidu_id'], pageNo=page_num, pageSize=1000)
orders = data_orders['orders']
for item in orders:
updatetime = await time_convert(item.get('updateTime')) if item.get('updateTime') else None
ns_dic = {
"id": uuid(),
"orderid": item.get("uuid"),
"ordertype": item.get("type"),
"accountid": item.get("accountId"),
"servicetype": item.get("serviceType"),
"producttype": item.get("productType"),
"shortids": ','.join(item['shortIds']) if item.get('shortIds') else '',
"price": item.get("price"),
"status": item.get("status"),
"autoreneworder": '1' if item.get("autoRenewOrder") else '0',
"createtime": await time_convert(item.get('createTime')) if item.get(
'createTime') else None,
"updatetime": updatetime
}
ns_exist_order = {
'orderid': item.get("uuid")
}
exist_order = await sor.R('baidu_orders', ns_exist_order)
if exist_order and exist_order[0]['updatetime'] != updatetime:
update_refund_sql = """UPDATE baidu_orders SET price = '%s', status = '%s', updatetime = '%s' WHERE orderid = '%s';""" % \
(item.get("price"), item.get("status"), updatetime,
item.get("uuid"))
await sor.sqlExe(update_refund_sql, {})
update_count += 1
if not exist_order:
await sor.C('baidu_orders', ns_dic)
add_count += 1
return {
'status': True,
'msg': '同步数据成功, 新增 %s 条, 更新 %s 条' % (add_count, update_count),
'data': {
'username': username
}
}
async def baidu_confirm_auto_renew_order(ns={}):
if ns.get('domain_name'):
orgid_li = await sor.R('reseller', {'domain_name': ns.get('domain_name'), 'del_flg': '0'})
orgid = orgid_li[0]['orgid']
else:
orgid = 'mIWUHBeeDM8mwAFPIQ8pS'
# 读取baidu_orders表格 确定状态是renew的订单ID
renew_results = []
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';"""
renew_results = await sor.sqlExe(renew_sql, {})
renew_status_count = 0
# 执行 get_baidu_orderlist sor分开运行
for renew_res in renew_results:
order_id = renew_res.get('orderid')
user_id = renew_res.get('user_id')
renew_status = await get_baidu_orderlist({'order_id': order_id, 'userid': user_id})
if renew_status.get('status'):
renew_status_count += 1
# 并触发update函数
update_msg = await update_baidu_order_list({'orgid': orgid})
return {
'status': True,
'msg': '续费订单执行结束, 触发了%s条数据' % len(renew_results),
'data': {
'renew_status_count': renew_status_count,
'update_msg': update_msg,
}
}
ret = await baidu_confirm_auto_renew_order(params_kw)
return ret

View File

@ -41,7 +41,7 @@ async def affirmbz_order(ns={}):
orgid = await sor.R('bz_order', {'id': ns['orderid']})
servicename = orgid[0]['servicename']
# product_url = None
product_url = None
# if ('BCC' in servicename) or ('GPU' in servicename):
# product_url = 'https://console.vcp.baidu.com/bcc/#/bcc/instance/list'

View File

@ -19,7 +19,7 @@ async def bb(ns={}):
# raise ValueError("供应商没有配置产品")
# 获取当前日期
current_date = datetime.datetime.now()
# current_date = datetime.datetime.now()
# 计算两个月后的日期
# future_date = current_date + dateutil.relativedelta.relativedelta(months=6)
@ -31,9 +31,18 @@ async def bb(ns={}):
# }
# users_id = await get_user()
# await asyncio.sleep(10)
import os
current_path = os.getcwd()
return {'current_date': current_path}
# import os
# current_path = os.getcwd()
# return {'current_date': current_path}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
await sor.U('white_list', {'id': ns.get('id'), 'user_id': ns.get('user_id')})
return {
'status': True,
'msg': 'Data inserted successfully',
'data': ''
}
ret = await bb(params_kw)
return ret

View File

@ -1,8 +1,20 @@
async def add_user_inquiry(ns={}):
if not ns.get('url_link'):
return {
'status': False,
'msg': '请传递url_link'
}
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:
ns_c = {
'id': uuid(),
'domain_name': domain_name,
'publish_type': ns.get('publish_type'),
'relate_id': ns.get('relate_id'),
'content': ns.get('content'),
'custom_type': ns.get('custom_type'),
'name': ns.get('name'),

View File

@ -151,7 +151,8 @@ detailDataDCI = {
},
]
}
]
],
"products": []
}
detailDataHL = {
@ -316,7 +317,8 @@ detailDataHL = {
}
]
}
]
],
"products": []
}
detailDataS = {
@ -471,7 +473,8 @@ detailDataS = {
}
]
}
]
],
"products": []
}
detailDataAI = {
@ -609,7 +612,8 @@ detailDataAI = {
}
]
}
]
],
"products": []
}
detailDataYITI = {
@ -766,7 +770,8 @@ detailDataYITI = {
},
]
},
]
],
"products": []
}
detailDataLJS = {
@ -868,7 +873,479 @@ detailDataLJS = {
"content": "2.GPU 加速 AI 推理,裸金属低延迟保障实时分析(如监控画面秒级报警);资源独占确保视频数据安全,符合安防 / 工业合规。"
}]
}
]
],
"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应用搭建、云上学习和测试等场景的服务。",
"price": "6000",
"pre_price": "7143",
"price_unit": "台/月",
"discount": 8.4,
"bgImgUrl": "https://www.kaiyuancloud.cn/idfile?path=firstpagebg/2.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-A100",
"description": "对象存储BOSBaidu Object Storage是一款稳定、安全、高效、高可拓展的云存储服务支持标准、低频、冷和归档存储等多种存储类型满足多场景的存储需求。用户可以将任意数量和形式的非结构化数据存入BOS并对数据进行管理和处理。",
"price": "31000",
"pre_price": None,
"price_unit": "台/月",
"discount": None,
"bgImgUrl": "https://www.kaiyuancloud.cn/idfile?path=firstpagebg/3.png",
"list": [
{
"id": 1,
"name": "CPU",
"content": "AMD EPYC 7543 32 C * 2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssc2x.png"
},
{
"id": 2,
"name": "内存",
"content": "64G DDR4-3200 * 32",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/cpu2x.png"
},
{
"id": 3,
"name": "GPU",
"content": "NVIDIA-A100-80GB-Nvlink * 8",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/sdcard2x.png"
},
{
"id": 4,
"name": "系统盘",
"content": "960G SATA SSD * 2",
"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": "MCX653105A - HDAT CX6 200G 1P HDR * 4",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssdr2x.png"
}
]
},
{
"title": "NVIDIA-A800",
"description": "云数据库 RDS为用户提供高性价比、安全可靠、性能卓越、易于维护的企业级云数据库服务。",
"price": "32000",
"pre_price": None,
"price_unit": "台/月",
"discount": None,
"bgImgUrl": "https://www.kaiyuancloud.cn/idfile?path=firstpagebg/4.png",
"list": [
{
"id": 1,
"name": "CPU",
"content": "Intel (R) Xeon (R) Platinum 8358 32 C * 2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssc2x.png"
},
{
"id": 2,
"name": "内存",
"content": "64G DDR4-3200 * 32",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/cpu2x.png"
},
{
"id": 3,
"name": "GPU",
"content": "NVIDIA-A800-80GB-Nvlink * 8",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/sdcard2x.png"
},
{
"id": 4,
"name": "系统盘",
"content": "960G SATA SSD * 2",
"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": "MCX653105A - HDAT CX6 200G 1P HDR *4",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssdr2x.png"
}
]
},
{
"title": "昇腾-910B",
"description": "裸金属服务器是一种融合了高性能 GPU 计算能力、裸金属服务器物理资源独占性及云计算弹性扩展特性的新型服务器形态。它既具备传统裸金属服务器的强劲性能与安全性,又能像云服务器一样按需灵活调整资源,适用于对算力、资源独占性和弹性要求极高的场景。",
"price": "22000",
"pre_price": "27500",
"price_unit": "台/月",
"discount": 8,
"bgImgUrl": "https://www.kaiyuancloud.cn/idfile?path=firstpagebg/5.png",
"list": [
{
"id": 23,
"name": "CPU",
"content": "鲲鹏920 * 4",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssc2x.png"
},
{
"id": 24,
"name": "内存",
"content": "32G DDR4-3200 * 32",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/cpu2x.png"
},
{
"id": 25,
"name": "GPU",
"content": "昇腾-910B-64GB * 8",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/sdcard2x.png"
},
{
"id": 26,
"name": "系统盘",
"content": "480G SATA SSD * 2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/servermini2x.png"
},
{
"id": 27,
"name": "数据盘",
"content": "3.84T U.2 NVMe SSD * 1",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/serverpath2x.png"
},
{
"id": 28,
"name": "网卡",
"content": "200G QSFP ROCE * 8",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssdr2x.png"
}
]
},
{
"title": "NVIDIA-4090",
"description": "High-performance GPU server with powerful computing capabilities, suitable for AI training, deep learning, etc.",
"price": "9000",
"pre_price": None,
"price_unit": "台/月",
"discount": None,
"bgImgUrl": "https://www.kaiyuancloud.cn/idfile?path=firstpagebg/4.png",
"list": [
{
"id": 1,
"name": "CPU",
"content": "Intel Xeon Gold 6430*2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssc2x.png"
},
{
"id": 2,
"name": "内存",
"content": "64G-DDR5 ECC*16",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/cpu2x.png"
},
{
"id": 3,
"name": "GPU",
"content": "4090-24G 涡轮版*8",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/sdcard2x.png"
},
{
"id": 4,
"name": "系统盘",
"content": "SATA-SSD-480G*2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/servermini2x.png"
},
{
"id": 5,
"name": "数据盘",
"content": "NVMe-SSD 3.84T*2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/serverpath2x.png"
},
{
"id": 6,
"name": "网卡",
"content": "25G 光口双口CX5*1",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssdr2x.png"
}
]
},
{
"title": "NVIDIA-4090-48G",
"description": "High-performance GPU server with powerful computing capabilities, suitable for AI training, deep learning, etc.",
"price": "8800",
"pre_price": None,
"price_unit": "台/月",
"discount": None,
"bgImgUrl": "https://www.kaiyuancloud.cn/idfile?path=firstpagebg/4.png",
"list": [
{
"id": 1,
"name": "CPU",
"content": "Intel Xeon 6530/32核心/2.1GHz*2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssc2x.png"
},
{
"id": 2,
"name": "内存",
"content": "64G DDR5 4800MHZ ECC REG*16",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/cpu2x.png"
},
{
"id": 3,
"name": "GPU",
"content": "8*RTX409048g涡轮;(直通模式)",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/sdcard2x.png"
},
{
"id": 4,
"name": "系统盘",
"content": "SATA-SSD-960G*1",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/servermini2x.png"
},
{
"id": 5,
"name": "数据盘",
"content": "NVMe-SSD 3.84T*2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/serverpath2x.png"
},
{
"id": 6,
"name": "网卡",
"content": "25G 光口双口CX5*1",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssdr2x.png"
}
]
},
{
"title": "NVIDIA-4090",
"description": "High-performance GPU server with powerful computing capabilities, suitable for AI training, deep learning, etc.",
"price": "8000",
"pre_price": None,
"price_unit": "台/月",
"discount": None,
"bgImgUrl": "https://www.kaiyuancloud.cn/idfile?path=firstpagebg/4.png",
"list": [
{
"id": 1,
"name": "CPU",
"content": "Intel Xeon 6530/32核心/2.1GHz *2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssc2x.png"
},
{
"id": 2,
"name": "内存",
"content": "64G DDR5 4800MHZ ECC REG*16",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/cpu2x.png"
},
{
"id": 3,
"name": "GPU",
"content": "8*RTX409024g涡轮;(直通模式)",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/sdcard2x.png"
},
{
"id": 4,
"name": "系统盘",
"content": "SATA-SSD-960G*1",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/servermini2x.png"
},
{
"id": 5,
"name": "数据盘",
"content": "NVMe-SSD 3.84T*2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/serverpath2x.png"
},
{
"id": 6,
"name": "网卡",
"content": "25G 光口双口CX5*1",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssdr2x.png"
}
]
},
{
"title": "NVIDIA-4090",
"description": "High-performance GPU server with powerful computing capabilities, suitable for AI training, deep learning, etc.",
"price": "7200",
"pre_price": None,
"price_unit": "台/月",
"discount": None,
"bgImgUrl": "https://www.kaiyuancloud.cn/idfile?path=firstpagebg/4.png",
"list": [
{
"id": 1,
"name": "CPU",
"content": "Intel Xeon 6530/32核心/2.1GHz *2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssc2x.png"
},
{
"id": 2,
"name": "内存",
"content": "32GB DDR4-3200ECCREG RDIMM *16",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/cpu2x.png"
},
{
"id": 3,
"name": "GPU",
"content": "Geforce RTX4090 24GPCI-e 涡轮GPU卡 *8",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/sdcard2x.png"
},
{
"id": 4,
"name": "系统盘",
"content": "2.5\"480G SATA 读取密集 SSD *2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/servermini2x.png"
},
{
"id": 5,
"name": "数据盘",
"content": "NVMe-SSD 1.92T *2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/serverpath2x.png"
},
{
"id": 6,
"name": "网卡",
"content": "25G 光口双口CX5 *2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssdr2x.png"
}
]
},
{
"title": "NVIDIA-4090",
"description": "High-performance GPU server with powerful computing capabilities, suitable for AI training, deep learning, etc.",
"price": "7000",
"pre_price": None,
"price_unit": "台/月",
"discount": None,
"bgImgUrl": "https://www.kaiyuancloud.cn/idfile?path=firstpagebg/4.png",
"list": [
{
"id": 1,
"name": "CPU",
"content": "AMD EPYC 7502/32核心/2.5GHz *1",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssc2x.png"
},
{
"id": 2,
"name": "内存",
"content": "64GB DDR4-3200ECCREG RDIMM *16",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/cpu2x.png"
},
{
"id": 3,
"name": "GPU",
"content": "Geforce RTX4090 24GPCI-e 涡轮GPU卡 *8",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/sdcard2x.png"
},
{
"id": 4,
"name": "系统盘",
"content": "2.5\"480G SATA 读取密集 SSD *1",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/servermini2x.png"
},
{
"id": 5,
"name": "数据盘",
"content": "NVMe-SSD 3.84T *2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/serverpath2x.png"
},
{
"id": 6,
"name": "网卡",
"content": "25G 光口双口CX5 *1",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssdr2x.png"
}
]
}
],
}
detailDataRQY = {
@ -969,7 +1446,8 @@ detailDataRQY = {
"content": "在处理大规模计算任务时,容器可以帮助管理和调度大量的计算任务,确保资源的高效利用‌"
}]
}
]
],
"products": []
}
detailDataLJS910B = {
@ -1088,7 +1566,8 @@ detailDataLJS910B = {
"content": "2.GPU 加速 AI 推理,裸金属低延迟保障实时分析(如监控画面秒级报警);资源独占确保视频数据安全,符合安防 / 工业合规。"
}]
}
]
],
"products": []
}
@ -1224,7 +1703,8 @@ detailDataYITIKUNLUN = {
"content": "与某运营商深度合作—— 技术测评领先、共建实验室、商业化落地,制定智能计算技术架构、评估AI 芯片功能/性能等指标、打造算力原生平台、为实验室提供强大算力底座,携手运营商解决智算发展中面临的挑战,共同实现智算技术引领。"
}]
}
]
],
"products": []
}
async def get_firstpage_net_detail(ns={}):

View File

@ -0,0 +1,80 @@
async def global_search(ns={}):
if not ns.get('url_link') or not ns.get('publish_type'):
return {
'status': False,
'msg': '请传递路由链接(url_link)和发布类型(publish_type)'
}
if not ns.get('keyword'):
return {
'status': False,
'msg': '请传递搜索关键词'
}
if not ns.get('display_page'):
return {
'status': False,
'msg': '请传递回显页面(display_page)'
}
# 增加分页功能
current_page = int(ns.get('current_page')) if ns.get('current_page') else 1
page_size = int(ns.get('page_size')) if ns.get('page_size') else 8
# 分页查询
offset = (current_page - 1) * page_size
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:
try:
# 增加分页条件
# 分页条件
find_sql = """SELECT * FROM user_publish_product WHERE publish_type = '%s' AND domain_name = '%s' AND audit_status = 'approved' AND listing_status = 'listing' AND del_flg = '0' ORDER BY priority DESC LIMIT %s OFFSET %s;"""
find_sql = find_sql % (ns.get('publish_type'), domain_name, page_size, offset)
# 添加keyword模糊查询 筛选product_name, requirement_summary
find_sql = find_sql.split('WHERE')[0] + ' WHERE ' + "(product_name LIKE '%%%%%s%%%%' OR requirement_summary LIKE '%%%%%s%%%%') AND " % (ns.get('keyword'), ns.get('keyword')) + find_sql.split('WHERE')[1]
# 如果是overview sql不筛选requirement_summary
if ns.get('display_page') == 'overview':
find_sql = find_sql.split('WHERE')[0] + ' WHERE ' + "(product_name LIKE '%%%%%s%%%%') AND " % (ns.get('keyword')) + find_sql.split('WHERE')[1]
# 计算筛选后 返回总数量 count(*)
count_sql = """SELECT COUNT(*) AS total_count FROM user_publish_product WHERE publish_type = '%s' AND domain_name = '%s' AND audit_status = 'approved' AND listing_status = 'listing' AND del_flg = '0' AND (product_name LIKE '%%%%%s%%%%' OR requirement_summary LIKE '%%%%%s%%%%');"""
count_sql = count_sql % (ns.get('publish_type'), domain_name, ns.get('keyword'), ns.get('keyword'))
count_result = await sor.sqlExe(count_sql, {})
total_count = count_result[0]['total_count']
result = await sor.sqlExe(find_sql, {})
for res in result:
if res.get('img') and res['img'] != 'null':
res['img'] = 'https://' + domain_name + '/idfile?path=' + res['img']
else:
res['img'] = None
# 根据display_page区分返回字段
if ns.get('display_page') == 'overview':
# result 产品名称去重后的 产品名称列表 根据名称去重
product_names = list(set([item['product_name'] for item in result]))
# 产品名称添加 uuid形成的八位id 形成列表嵌套字典的格式
result = [{ 'id': str(uuid())[:8], 'product_name': product_name} for product_name in product_names]
return {
'status': True,
'msg': 'search success',
'data': {
'current_page': current_page,
'page_size': page_size,
'total_count': total_count,
'result': result
}
}
except Exception as e:
return {
'status': False,
'msg': 'Failed to search publish product, %s' % str(e)
}
ret = await global_search(params_kw)
return ret

View File

@ -0,0 +1,49 @@
async def homepage_category_tree_add(ns={}):
if not ns.get('url_link'):
return {
'status': False,
'msg': '请传递路由链接'
}
domain_name = ns.get('url_link').split("//")[1].split("/")[0]
if 'localhost' in domain_name:
domain_name = 'dev.opencomputing.cn'
if ns.get('userid'):
userid = ns.get('userid')
else:
userid = await get_user()
db = DBPools()
async with db.sqlorContext('kboss') as sor:
# 区分运营和普通客户
user_list = await sor.R('users', {'id': userid})
if not user_list:
return {
'status': False,
'msg': '没有找到匹配的用户'
}
orgid = user_list[0]['orgid']
ns_dic = {
'id': uuid(),
'domain_name': domain_name,
'orgid': orgid,
'parentid': ns.get('parentid', None),
'priority': ns.get('priority', 99),
'name': ns.get('name', ''),
'icon': ns.get('icon', None),
'source': ns.get('source', None),
'listurl': ns.get('listurl', None),
}
try:
await sor.C('homepage_category_tree', ns_dic)
return {
'status': True,
'msg': 'product category created successfully'
}
except Exception as e:
return {
'status': False,
'msg': 'Failed to create product category, %s' % str(e)
}
ret = await homepage_category_tree_add(params_kw)
return ret

View File

@ -0,0 +1,35 @@
async def homepage_category_tree_search(ns={}):
if not ns.get('url_link'):
return {
'status': False,
'msg': '请传递路由链接'
}
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:
ns_dic = {
'domain_name': domain_name,
'del_flg': '0'
}
try:
category_list = await sor.R('homepage_category_tree', ns_dic)
# 对icon字段增加前缀
for category in category_list:
if category.get('iconurl') and ('http' not in category['iconurl']):
category['iconurl'] = 'https://' + domain_name + category['iconurl']
return {
'status': True,
'msg': 'product category created successfully',
'data': category_list
}
except Exception as e:
return {
'status': False,
'msg': 'Failed to create product category, %s' % str(e)
}
ret = await homepage_category_tree_search(params_kw)
return ret

View File

@ -0,0 +1,43 @@
async def homepage_category_tree_update(ns={}):
if not ns.get('url_link'):
return {
'status': False,
'msg': '请传递路由链接'
}
if not ns.get('id'):
return {
'status': False,
'msg': '请传递分类ID'
}
domain_name = ns.get('url_link').split("//")[1].split("/")[0]
if 'localhost' in domain_name:
domain_name = 'dev.opencomputing.cn'
if ns.get('userid'):
userid = ns.get('userid')
else:
userid = await get_user()
db = DBPools()
async with db.sqlorContext('kboss') as sor:
# 区分运营和普通客户
user_list = await sor.R('users', {'id': userid})
if not user_list:
return {
'status': False,
'msg': '没有找到匹配的用户'
}
orgid = user_list[0]['orgid']
ns.pop('userid', None)
try:
await sor.U('homepage_category_tree', ns)
return {
'status': True,
'msg': 'product category updated successfully'
}
except Exception as e:
return {
'status': False,
'msg': 'Failed to update product category, %s' % str(e)
}
ret = await homepage_category_tree_update(params_kw)
return ret

View File

@ -45,8 +45,9 @@ async def publish_product_add(ns={}):
ns_dic = {
"id": uuid(),
"publish_type": ns.get("publish_type"),
"cart_flag": ns.get("cart_flag"),
"orgid": orgid,
"img": ns.get('img'),
"img": ns.get('img') if ns.get('img') else None,
"domain_name": domain_name,
"product_name": ns.get("product_name"),
"product_category": product_category,
@ -65,6 +66,7 @@ async def publish_product_add(ns={}):
"price": ns.get("price"),
"unit": ns.get("unit"),
"discount": ns.get("discount"),
"service_charge": ns.get("service_charge"),
"short_term": ns.get("short_term"),
"priority": ns.get("priority") if ns.get("priority") else 1,
"status": ns.get("status") if ns.get("status") else '0',
@ -79,7 +81,7 @@ async def publish_product_add(ns={}):
if ns.get('discount') == '0':
ns['discount'] = None
ns_dic['discount'] = None
if ns.get('price') and ns.get('discount'):
ns_dic['discount_price'] = float(ns.get('price')) * float(ns['discount']) / 10
else:

View File

@ -1,20 +1,63 @@
async def get_user_role(ns={}):
sor = ns['sor']
# get role
ns['del_flg'] = '0'
res_role = await sor.R('userrole', ns)
if res_role:
user_role = res_role[0]
else:
return {
"status": False,
"msg": "userrole table, user id can not find..."
}
roleid = user_role.get('roleid')
# get role name
role_name = await sor.R('role', {'id': roleid})
if role_name:
role = role_name[0].get('role')
else:
return {
"status": False,
"msg": "role table, can not get role name"
}
return role
async def publish_product_search(ns={}):
"""
普通客户查看
运营查看自己提交内容
运营查看所有用户内容
运营: 待审 已审 上架
客户: 已审 上架 驳回
:param ns:
:return:
"""
offset = ns['offset']
flag = ns.get('flag')
offset = ns.get('offset')
manager_self = ns.get('manager_self')
if ns.get('userid'):
userid = ns.get('userid')
else:
userid = await get_user()
if not ns.get('url_link'):
return {
'status': False,
'msg': 'url_link is required'
}
domain_name = ns.get('url_link').split("//")[1].split("/")[0]
if 'localhost' in domain_name:
domain_name = 'dev.opencomputing.cn'
publish_type = ns.get('publish_type')
page_size = int(ns['page_size']) if ns.get('page_size') else 8
current_page_param = int(ns['current_page']) if ns.get('current_page') else 1
current_page = (current_page_param - 1) * page_size
db = DBPools()
async with db.sqlorContext('kboss') as sor:
async with db.sqlorContext('kboss') as sor:
# 区分运营和普通客户
user_list = await sor.R('users', {'id': userid})
if not user_list:
@ -31,32 +74,83 @@ async def publish_product_search(ns={}):
if user_role != '客户':
ns['del_flg'] = '0'
# 业主机构角色并且是只查看业主机构自己 flag==single
if orgid == 'mIWUHBeeDM8mwAFPIQ8pS' and flag == 'single':
# 业主机构角色并且是只查看业主机构自己 manager_self==single
if orgid == 'mIWUHBeeDM8mwAFPIQ8pS' and manager_self == 'single':
count_sql = """SELECT COUNT(*) AS total_count, upr.* FROM user_publish_product AS upr LEFT JOIN organization AS org ON upr.orgid = org.id
WHERE org.parentid IS NULL AND upr.del_flg = '0' AND upr.publish_type = '%s' ORDER BY upr.create_at DESC;""" % publish_type
find_sql = """SELECT upr.* FROM user_publish_product AS upr LEFT JOIN organization AS org ON upr.orgid = org.id
WHERE org.parentid IS NULL AND upr.del_flg = '0' ORDER BY upr.create_at DESC LIMIT 20 OFFSET %s;""" % offset
# 业主机构角色并且是查看所有(包括业主机构自己) flag!=single
WHERE org.parentid IS NULL AND upr.del_flg = '0' AND upr.publish_type = '%s' ORDER BY upr.create_at DESC LIMIT %s OFFSET %s;""" % (publish_type, page_size, current_page)
# 业主机构角色并且是查看所有(不包括业主机构自己) manager_self!=single (WHERE (org.id = '%s' or org.parentid = '%s')
elif orgid == 'mIWUHBeeDM8mwAFPIQ8pS':
count_sql = """SELECT COUNT(*) AS total_count, upr.* FROM user_publish_product AS upr LEFT JOIN organization AS org ON upr.orgid = org.id
WHERE org.parentid = '%s' AND org.org_type != '1' AND upr.del_flg = '0' AND upr.publish_type = '%s' ORDER BY upr.create_at DESC;""" % (orgid, publish_type)
find_sql = """SELECT upr.* FROM user_publish_product AS upr LEFT JOIN organization AS org ON upr.orgid = org.id
WHERE org.id = '%s' or org.parentid = '%s' AND org.org_type != '1' AND upr.del_flg = '0' ORDER BY upr.create_at DESC LIMIT 20 OFFSET %s;""" % (orgid, orgid, offset)
WHERE org.parentid = '%s' AND org.org_type != '1' AND upr.del_flg = '0' AND upr.publish_type = '%s' ORDER BY upr.create_at DESC LIMIT %s OFFSET %s;""" % (orgid, publish_type, page_size, current_page)
# 其他机构非用户角色 只查看自己
elif flag == 'single':
elif manager_self == 'single':
count_sql = """SELECT COUNT(*) AS total_count, upr.* FROM user_publish_product AS upr LEFT JOIN organization AS org ON upr.orgid = org.id
WHERE org.id = '%s' AND upr.del_flg = '0' AND upr.publish_type = '%s' ORDER BY upr.create_at DESC;""" % (orgid, publish_type)
find_sql = """SELECT upr.* FROM user_publish_product AS upr LEFT JOIN organization AS org ON upr.orgid = org.id
WHERE org.id = '%s' AND upr.del_flg = '0' ORDER BY upr.create_at DESC LIMIT 20 OFFSET %s;""" % (orgid, offset)
# 其他机构非用户角色查看所有
WHERE org.id = '%s' AND upr.del_flg = '0' AND upr.publish_type = '%s' ORDER BY upr.create_at DESC LIMIT %s OFFSET %s;""" % (orgid, publish_type, page_size, current_page)
# 其他机构非用户角色查看所有客户 (WHERE (org.id = '%s' or org.parentid = '%s') AND upr.del_flg = '0')
else:
count_sql = """SELECT COUNT(*) AS total_count, upr.* FROM user_publish_product AS upr LEFT JOIN organization AS org ON upr.orgid = org.id
WHERE org.parentid = '%s' AND upr.del_flg = '0' AND upr.publish_type = '%s' ORDER BY upr.create_at DESC;""" % (orgid, publish_type)
find_sql = """SELECT upr.* FROM user_publish_product AS upr LEFT JOIN organization AS org ON upr.orgid = org.id
WHERE (org.id = '%s' or org.parentid = '%s') AND upr.del_flg = '0' ORDER BY upr.create_at DESC LIMIT 20 OFFSET %s;""" % (orgid, orgid, offset)
WHERE org.parentid = '%s' AND upr.del_flg = '0' AND upr.publish_type = '%s' ORDER BY upr.create_at DESC LIMIT %s OFFSET %s;""" % (orgid, publish_type, page_size, current_page)
# 客户角色
else:
ns['del_flg'] = '0'
ns['orgid'] = user_list[0]['id']
find_sql = """SELECT * FROM user_publish_product WHERE orgid = '%s' AND del_flg = '0' ORDER BY create_at DESC LIMIT 20 OFFSET %s;""" % (orgid, offset)
count_sql = """SELECT COUNT(*) AS total_count, upr.* FROM user_publish_product AS upr WHERE upr.orgid = '%s' AND upr.del_flg = '0' AND upr.publish_type = '%s' ORDER BY upr.create_at DESC LIMIT %s OFFSET %s;""" % (orgid, publish_type, page_size, current_page)
find_sql = """SELECT upr.* FROM user_publish_product AS upr WHERE upr.orgid = '%s' AND upr.del_flg = '0' AND upr.publish_type = '%s' ORDER BY upr.create_at DESC LIMIT %s OFFSET %s;""" % (orgid, publish_type, page_size, current_page)
# 截取count_sql和find_sql中的 WHERE, 向WHERE后追加新的筛选条件
# 如果审核状态是pending,approved, 查询待审和已审的数据
if ns.get('audit_status') == 'pending,approved':
count_sql = count_sql.split('WHERE')[0] + ' WHERE ' + "(upr.audit_status = 'pending' OR upr.audit_status = 'approved') AND " + count_sql.split('WHERE')[1]
find_sql = find_sql.split('WHERE')[0] + ' WHERE ' + "(upr.audit_status = 'pending' OR upr.audit_status = 'approved') AND " + find_sql.split('WHERE')[1]
elif ns.get('audit_status'):
count_sql = count_sql.split('WHERE')[0] + ' WHERE ' + "upr.audit_status = '%s' AND " % ns.get('audit_status') + count_sql.split('WHERE')[1]
find_sql = find_sql.split('WHERE')[0] + ' WHERE ' + "upr.audit_status = '%s' AND " % ns.get('audit_status') + find_sql.split('WHERE')[1]
if ns.get('listing_status'):
count_sql = count_sql.split('WHERE')[0] + ' WHERE ' + "upr.listing_status = '%s' AND " % ns.get('listing_status') + count_sql.split('WHERE')[1]
find_sql = find_sql.split('WHERE')[0] + ' WHERE ' + "upr.listing_status = '%s' AND " % ns.get('listing_status') + find_sql.split('WHERE')[1]
# 添加start_date和end_date筛选
if ns.get('start_date') and ns.get('end_date'):
count_sql = count_sql.split('WHERE')[0] + ' WHERE ' + "(upr.create_at BETWEEN '%s' AND '%s') AND " % (ns.get('start_date'), ns.get('end_date')) + count_sql.split('WHERE')[1]
find_sql = find_sql.split('WHERE')[0] + ' WHERE ' + "(upr.create_at BETWEEN '%s' AND '%s') AND " % (ns.get('start_date'), ns.get('end_date')) + find_sql.split('WHERE')[1]
# 添加keyword模糊查询 只筛选产品名称
if ns.get('keyword'):
count_sql = count_sql.split('WHERE')[0] + ' WHERE ' + "upr.product_name LIKE '%%%%%s%%%%' AND " % ns.get('keyword') + count_sql.split('WHERE')[1]
find_sql = find_sql.split('WHERE')[0] + ' WHERE ' + "upr.product_name LIKE '%%%%%s%%%%' AND " % ns.get('keyword') + find_sql.split('WHERE')[1]
print(count_sql)
total_count = (await sor.sqlExe(count_sql, {}))[0]['total_count']
result = await sor.sqlExe(find_sql, {})
# 处理图片
for res in result:
if res.get('img') and res['img'] != 'null':
res['img'] = 'https://' + domain_name + '/idfile?path=' + res['img']
else:
res['img'] = None
return {
'status': True,
'msg': 'Requirements retrieved successfully',
'data': result
'data': {
"id": uuid(),
"total_count": total_count,
"page_size": page_size,
"current_page": current_page_param,
"product_list": result
}
}
except Exception as e:
return {

View File

@ -0,0 +1,67 @@
async def get_user_role(ns={}):
sor = ns['sor']
# get role
ns['del_flg'] = '0'
res_role = await sor.R('userrole', ns)
if res_role:
user_role = res_role[0]
else:
return {
"status": False,
"msg": "userrole table, user id can not find..."
}
roleid = user_role.get('roleid')
# get role name
role_name = await sor.R('role', {'id': roleid})
if role_name:
role = role_name[0].get('role')
else:
return {
"status": False,
"msg": "role table, can not get role name"
}
return role
async def publish_product_search_detail(ns={}):
"""
:param ns:
:return:
"""
if ns.get('userid'):
userid = ns.get('userid')
else:
userid = await get_user()
db = DBPools()
async with db.sqlorContext('kboss') as sor:
user_role = None
# 区分运营和普通客户
if userid:
user_list = await sor.R('users', {'id': userid})
orgid = user_list[0]['orgid']
user_role = await get_user_role({'userid': userid, 'sor': sor})
# 如果用户角色是运营并且from='b' product_list中的phone_number和email做加星号处理
product_list = await sor.R('user_publish_product', {'id': ns.get('id'), 'del_flg': '0'})
if user_role == '运营' and ns.get('from') == 'b':
pass
else:
for product in product_list:
if product.get('phone_number'):
product['phone_number'] = product['phone_number'][:3] + '****' + product['phone_number'][7:]
else:
product['phone_number'] = '198****8601'
if product.get('email'):
product['email'] = product['email'][:2] + '****' + product['email'][6:]
else:
product['email'] = 'kawa@****.com'
return {
'status': True,
'msg': 'Product retrieved successfully',
'data': product_list[0]
}
ret = await publish_product_search_detail(params_kw)
return ret

View File

@ -44,8 +44,10 @@ async def publish_product_search_first_page(ns={}):
list2 = [item.strip() for item in res['company_type'].split(',')]
if not bool(set(list1) & set(list2)):
continue
res['img'] = 'https://' + domain_name + '/idfile?path=' + res['img']
if res.get('img') and res['img'] != 'null':
res['img'] = 'https://' + domain_name + '/idfile?path=' + res['img']
else:
res['img'] = None
# 电话和邮箱模糊化处理
if res.get('phone_number'):

View File

@ -0,0 +1,120 @@
async def publish_product_to_excel(ns={}):
if not ns.get('ids') and (not ns.get('result')):
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 == '1':
value = '上架'
elif value == '0':
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
}
ret = await publish_product_to_excel(params_kw)
return ret

View File

@ -0,0 +1,123 @@
async def get_user_role(ns={}):
sor = ns['sor']
# get role
ns['del_flg'] = '0'
res_role = await sor.R('userrole', ns)
if res_role:
user_role = res_role[0]
else:
return {
"status": False,
"msg": "userrole table, user id can not find..."
}
roleid = user_role.get('roleid')
# get role name
role_name = await sor.R('role', {'id': roleid})
if role_name:
role = role_name[0].get('role')
else:
return {
"status": False,
"msg": "role table, can not get role name"
}
return role
async def publish_product_update(ns={}):
"""
运营审批
运营编辑
客户编辑
运营发布
"""
if ns.get('userid'):
userid = ns.get('userid')
else:
userid = await get_user()
db = DBPools()
async with db.sqlorContext('kboss') as sor:
# 区分运营和普通客户
user_list = await sor.R('users', {'id': userid})
if not user_list:
return {
'status': False,
'msg': '没有找到匹配的用户'
}
# 处理图片 如果包含http就跳过
if ns.get('img') and ns['img'].startswith('http'):
ns.pop('img', None)
orgid = user_list[0]['orgid']
ns.pop('userid', None)
ns_dic = ns
if ns.get('discount') == '0':
ns['discount'] = None
ns_dic['discount'] = None
if ns.get('price') and ns.get('discount'):
ns_dic['discount_price'] = float(ns.get('price')) * float(ns['discount']) / 10
elif ns.get('price'):
ns_dic['discount_price'] = ns['price']
else:
pass
user_role = await get_user_role({'userid': userid, 'sor': sor})
# 编辑状态
# 非客户角色编辑客户的产品
if user_role != '客户' and ns.get('orgid') != orgid:
# 非客户角色审核客户的产品
# 如果是待审状态 更新审核状态为通过 上架状态为上架
if ns.get('audit_status') == 'approved':
ns_dic['audit_status'] = 'approved'
ns_dic['listing_status'] = 'listing'
# 如果是上架商品 更新publish_time为当前时间
if ns.get('listing_status') == 'listing':
ns_dic['publish_time'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# 如果是下架商品 更新publish_time为None
elif ns.get('listing_status') == 'delisting':
ns_dic['publish_time'] = None
# 如果是删除商品 更新publish_time为None
elif ns.get('del_flg') == '1':
ns_dic['publish_time'] = None
else:
ns_dic['publish_time'] = None
# 非客户角色编辑自己的产品 默认审批通过和发布成功
elif user_role != '客户':
# 运营删除自己的产品
if ns.get('del_flg') == '1':
pass
else:
ns['status'] = '1'
ns_dic['audit_status'] = 'approved'
# 运营下架自己的产品
if not ns.get('listing_status') == 'delisting':
ns_dic['listing_status'] = 'listing'
ns_dic['first_page'] = '1'
# 客户编辑以后 审核状态是: 待审 上架状态是:空
else:
# 客户要下架自己的产品
if ns.get('listing_status') == 'delisting':
ns_dic['audit_status'] = None
ns_dic['listing_status'] = 'delisting'
# 客户仅编辑不是下架产品
else:
ns_dic['audit_status'] = 'pending'
ns_dic['listing_status'] = None
ns_dic['publish_time'] = None
try:
await sor.U('user_publish_product', ns_dic)
return {
'status': True,
'msg': 'publish product created successfully'
}
except Exception as e:
return {
'status': False,
'msg': 'Failed to publish product, %s' % str(e)
}
ret = await publish_product_update(params_kw)
return ret

View File

@ -1,7 +1,16 @@
async def search_user_inquiry(ns={}):
if not ns.get('url_link'):
return {
'status': False,
'msg': '请传递url_link'
}
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:
search_sql = """select * from product_inquiry order by create_at desc;"""
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, {})
return {
'status': True,

View File

@ -0,0 +1,45 @@
async def enterprise_audit_info_add(ns={}):
db = DBPools()
async with db.sqlorContext('kboss') as sor:
# 检查orgid是否存在
if ns.get('userid'):
userid = ns.get('userid')
else:
userid = await get_user()
user_list = await sor.R('users', {'id': userid})
if not user_list:
return {
'status': False,
'msg': 'user not found'
}
orgid = user_list[0]['orgid']
ns_dic = {
'id': uuid(),
'orgid': orgid,
'account_type': ns.get('account_type'),
'company_name': ns.get('company_name'),
'license_number': ns.get('license_number'),
'license_original_img': ns.get('license_original_img'),
'license_copy_img': ns.get('license_copy_img'),
'office_address': ns.get('office_address'),
'registered_address': ns.get('registered_address'),
'contact_name': ns.get('contact_name'),
'telephone': ns.get('telephone'),
'mobile_phone': ns.get('mobile_phone'),
'email': ns.get('email'),
'audit_status': 'pending', # 默认待审
}
try:
await sor.C('enterprise_audit_info', ns_dic)
return {
'status': True,
'msg': 'enterprise audit info created successfully'
}
except Exception as e:
return {
'status': False,
'msg': 'Failed to create enterprise audit info, %s' % str(e)
}
ret = await enterprise_audit_info_add(params_kw)
return ret

View File

@ -0,0 +1,78 @@
async def get_user_role(ns={}):
sor = ns['sor']
# get role
ns['del_flg'] = '0'
res_role = await sor.R('userrole', ns)
if res_role:
user_role = res_role[0]
else:
return {
"status": False,
"msg": "userrole table, user id can not find..."
}
roleid = user_role.get('roleid')
# get role name
role_name = await sor.R('role', {'id': roleid})
if role_name:
role = role_name[0].get('role')
else:
return {
"status": False,
"msg": "role table, can not get role name"
}
return role
async def enterprise_audit_info_search(ns={}):
if not ns.get('url_link'):
return {
'status': False,
'msg': '请传递url_link'
}
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:
# 检查orgid是否存在
if ns.get('userid'):
userid = ns.get('userid')
else:
userid = await get_user()
user_list = await sor.R('users', {'id': userid})
if not user_list:
return {
'status': False,
'msg': 'user not found'
}
orgid = user_list[0]['orgid']
user_role = await get_user_role({'userid': userid, 'sor': sor})
try:
if user_role == '客户': # 客户查询
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
# 执行查询
res = await sor.sqlExe(find_sql, {})
# 处理结果中的图片路径 增加前缀
for item in res:
if item.get('license_original_img'):
item['license_original_img'] = 'https://' + domain_name + '/idfile?path=' + item['license_original_img']
if item.get('license_copy_img'):
item['license_copy_img'] = 'https://' + domain_name + '/idfile?path=' + item['license_copy_img']
return {
'status': True,
'msg': 'enterprise audit info search successfully',
'data': res
}
except Exception as e:
return {
'status': False,
'msg': 'Failed to search enterprise audit info, %s' % str(e)
}
ret = await enterprise_audit_info_search(params_kw)
return ret

View File

@ -0,0 +1,70 @@
async def get_user_role(ns={}):
sor = ns['sor']
# get role
ns['del_flg'] = '0'
res_role = await sor.R('userrole', ns)
if res_role:
user_role = res_role[0]
else:
return {
"status": False,
"msg": "userrole table, user id can not find..."
}
roleid = user_role.get('roleid')
# get role name
role_name = await sor.R('role', {'id': roleid})
if role_name:
role = role_name[0].get('role')
else:
return {
"status": False,
"msg": "role table, can not get role name"
}
return role
async def enterprise_audit_info_update(ns={}):
db = DBPools()
async with db.sqlorContext('kboss') as sor:
# 检查orgid是否存在
if ns.get('userid'):
userid = ns.get('userid')
else:
userid = await get_user()
user_list = await sor.R('users', {'id': userid})
if not user_list:
return {
'status': False,
'msg': 'user not found'
}
user_role = await get_user_role({'userid': userid, 'sor': sor})
ns.pop('userid', None)
# ns['orgid'] = orgid
try:
if user_role == '客户': # 客户更新
ns['audit_status'] = 'pending' # 客户更新时默认审核状态为待审
else: # 运营更新 如果是approved reject_reason为空
if ns.get('audit_status') == 'approved':
ns['reject_reason'] = None
# 生成更新的sql语句
update_sql = """UPDATE enterprise_audit_info SET """
for key, value in ns.items():
if value is None:
update_sql += f"{key} = NULL, "
continue
update_sql += f"{key} = '{value}', "
update_sql = update_sql[:-2] + f" WHERE id = '{ns.get('id')}';"""
await sor.sqlExe(update_sql, {})
return {
'status': True,
'msg': '更新成功'
}
except Exception as e:
return {
'status': False,
'msg': 'Failed to update enterprise audit info, %s' % str(e)
}
ret = await enterprise_audit_info_update(params_kw)
return ret

View File

@ -84,8 +84,44 @@ export function reqSearchByMangement(data){
return request({
url: '/product/publish_product_search.dspy',
method: 'post',
headers: { 'Content-Type': 'application/json' },
headers: { 'Content-Type': 'application/js1on' },
data
})
}
//编辑商品
export function reqEditProduct(data){
return request({
url: '/product/publish_product_update.dspy',
method: 'post',
headers: { 'Content-Type': 'multipart/form-data' },
data
})
}
//编辑非图片字段
export function reqEditProductNoImg(data){
return request({
url: '/product/publish_product_update.dspy',
method: 'post',
headers: { 'Content-Type': 'application/js1on' },
data
})
}
//搜索
export function reqSearch(data){
return request({
url: '/product/global_search.dspy',
method: 'post',
headers: { 'Content-Type': 'application/js1on' },
data
})
}
//导出 //reqExportProduct
export function reqExportProduct(data){
return request({
url: '/product/publish_product_to_excel.dspy',
method: 'post',
headers: { 'Content-Type': 'application/js1on' },
data
})
}

View File

@ -65,25 +65,6 @@ export const constantRoutes = [
component: () => import('@/views/beforeLogin/index.vue'),
hidden: true
},
{
hidden: true,
alwaysShow: 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: '/wxPage',
@ -204,6 +185,13 @@ export const constantRoutes = [
hidden: true,
meta: { title: "算力供需广场", fullPath: "/ncmatch/supplyAndDemandSquare" },
},
{
path: "search",
component: () => import("@/views/homePage/ncmatch/searchBox/index.vue"),
name: "search",
hidden: true,
meta: { title: "产品查询", fullPath: "/ncmatch/searchBox" },
},
]
},
{
@ -313,6 +301,89 @@ 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",
@ -747,7 +818,6 @@ export const asyncRoutes = [
meta: { title: "工单管理", fullPath: "/customer/workOrderManagement" },
},
{
hidden: true,
path: 'approve',
component: () => import('@/views/customer/ncApprove/index.vue'),
name: "Approve",
@ -839,7 +909,7 @@ export const asyncRoutes = [
), name: "userResource", meta: { title: "资源实例", fullPath: "/customer/userResource", noCache: true },
},
{
hidden: true,
path: "sshTerminal",
component: () => import(
// "@/views/customer/userResource/iframeJiNan.vue"//iframe报错

View File

@ -0,0 +1,6 @@
import Vue from 'vue'
// 创建事件总线实例
const eventBus = new Vue()
export default eventBus

View File

@ -0,0 +1,26 @@
<template>
<div>
<commonBox :role="role"></commonBox>
</div>
</template>
<script>
//| |
import commonBox from '@/views/customer/productMangement/commonBox/index.vue';
export default {
name: 'productList',
components: { commonBox },
data() {
return {
role:{
role_type:'customer',
audit_status:'approved'
}
}
},
created() {
},
}
</script>

View File

@ -0,0 +1,26 @@
<template>
<div>
<commonBox :role="role"></commonBox>
</div>
</template>
<script>
//| |
import commonBox from '@/views/customer/productMangement/commonBox/index.vue';
export default {
name: 'productList',
components: { commonBox },
data() {
return {
role:{
role_type:'customer',
audit_status:'pending'
}
}
},
created() {
},
}
</script>

View File

@ -0,0 +1,36 @@
<template>
<div>
<commonBox :role="role"></commonBox>
</div>
</template>
<script>
//| |
import commonBox from '@/views/customer/productMangement/commonBox/index.vue';
export default {
name: 'productList',
components: { commonBox },
data() {
return {
role:{
role_type:'customer',
audit_status:'',
publish_type:null
}
}
},
created() {
if(sessionStorage.getItem('jueseNew').includes('运营')){
console.log("运营");
this.role.role_type='customer'
}else if(sessionStorage.getItem('jueseNew').includes('客户')){
console.log("客户");
this.role.role_type='user'
}
console.log("role",this.role);
this.role.audit_status='pending,approved'
this.role.publish_type='2'
},
}
</script>

View File

@ -0,0 +1,36 @@
<template>
<div>
<commonBox :role="role"></commonBox>
</div>
</template>
<script>
//| |
import commonBox from '@/views/customer/productMangement/commonBox/index.vue';
export default {
name: 'productList',
components: { commonBox },
data() {
return {
role:{
role_type:'customer',
audit_status:'',
publish_type:null
}
}
},
created() {
if(sessionStorage.getItem('jueseNew').includes('运营')){
console.log("运营");
this.role.role_type='customer'
}else if(sessionStorage.getItem('jueseNew').includes('客户')){
console.log("客户");
this.role.role_type='user'
}
console.log("role",this.role);
this.role.audit_status='rejected'
this.role.publish_type='2'
},
}
</script>

View File

@ -12,7 +12,7 @@
</li>
<li>
<el-input size="mini" v-model="searchData.keyword" placeholder="请输入商品名称或关键词" clearable></el-input>
<el-input size="mini" v-model="searchData.keyword" placeholder="请输入关键词" clearable></el-input>
</li>
<li>
<el-button size="mini" @click="resetSearch">重置</el-button>
@ -22,17 +22,21 @@
</div>
<div class="table-box">
<div style="margin-bottom: 10px;">
<el-radio-group v-model="searchData.radioType" class="radio-group" size="mini"
@change="handleRadioChange">
<el-radio-group v-if="role.role_type == 'customer'" 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 }}
</el-radio-button>
</el-radio-group>
<el-button style="margin-left: 10px;" size="mini" @click="exportData">
<!-- <el-button style="margin-left: 10px;" size="mini" @click="openAddDialog">
<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> -->
</div>
<el-table height="calc(100vh - 210px)" v-loading="loading" :data="tableData"
<el-table 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">
@ -49,14 +53,29 @@
</el-table-column>
<el-table-column prop="product_category" show-overflow-tooltip label="所属类别" min-width="180">
</el-table-column>
<el-table-column show-overflow-tooltip label="审核状态" min-width="180">
<template slot-scope="scope">
<el-tag v-if="scope.row.audit_status === 'approved'" type="success">审核通过</el-tag>
<el-tag v-else-if="scope.row.audit_status === 'rejected'" type="danger">审核不通过</el-tag>
<el-tag v-else type="warning">待审核</el-tag>
</template>
</el-table-column>
<el-table-column show-overflow-tooltip label="上架状态" min-width="180">
<template slot-scope="scope">
<el-tag v-if="scope.row.listing_status === 'listing'" type="success">已上架</el-tag>
<el-tag v-else-if="scope.row.listing_status === 'delisting'" type="danger">已下架</el-tag>
<el-tag v-else type="warning">未上架</el-tag>
</template>
</el-table-column>
<el-table-column prop="company_name" show-overflow-tooltip label="所属企业" min-width="180">
</el-table-column>
<el-table-column prop="company_type" show-overflow-tooltip label="公司类别" min-width="180">
</el-table-column>
<el-table-column prop="address" show-overflow-tooltip label="商品概述" min-width="180">
</el-table-column>
<el-table-column prop="address" show-overflow-tooltip label="商品详情" min-width="180">
<!-- <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">
@ -65,12 +84,27 @@
</el-table-column>
<el-table-column fixed="right" min-width="240" label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="openDetail(scope.row)">查看</el-button>
<el-button type="text" style="color: #409EFF;" size="small">修改</el-button>
<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 type="text" style="color: #67C23A;" size="small">上架</el-button>
<el-button type="text" size="small">下架</el-button>
<el-button type="text" style="color: #F56C6C;" size="small">删除</el-button>
<el-button
:disabled="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'"
@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="确定删除该条目吗?"
@confirm="deleteProduct(scope.row)">
<el-button slot="reference" type="text" style="color: #F56C6C;margin-left: 10px;"
size="small">删除</el-button>
</el-popconfirm>
</template>
</el-table-column>
@ -80,21 +114,36 @@
layout="total, prev, pager, next" :total="total_count">
</el-pagination>
</div>
<commonDetail
:visible="showProductDetail"
@close="handleDetailClose"
@approve="handleApprove"
@reject="handleReject">
<commonDetail :showType="detailType" :visible="showProductDetail" @close="handleDetailClose"
@approve="handleApprove" @reject="handleReject">
</commonDetail>
<!-- 编辑商品弹窗 -->
<el-dialog :title="editForm.publish_type === '1' ? '编辑商品' : '编辑需求'" :visible.sync="showEditDialog" width="80%"
top="5vh">
<sendProduct :publish_type="editForm.publish_type" :isEdit="true" :editData="editForm"
@success="handleEditSuccess" @close="closeEditDialog">
</sendProduct>
</el-dialog>
<!-- 添加商品/需求弹窗 -->
<el-dialog :title="publish_type === '2' ? '添加需求' : '添加商品'" width="80%" top="5vh"
:visible.sync="sendProductVisible">
<sendProduct :publish_type="publish_type" :isEdit="false" @success="sendProductSuccess"
@close="closeAddDialog">
</sendProduct>
</el-dialog>
</div>
</template>
<script>
import { reqSearchByMangement, reqGetProductDetail } from '@/api/ncmatch';
import XLSX from 'xlsx';
import { reqSearchByMangement, reqGetProductDetail, reqEditProduct, reqEditProductNoImg, reqExportProduct } from '@/api/ncmatch';
import commonDetail from '@/views/customer/productMangement/commonDetail/index.vue';
import { mapState } from 'vuex';
import sendProduct from '@/views/homePage/ncmatch/mainPage/sendProduct/index.vue';
export default {
name: 'commonBox',
components: { commonDetail },
components: { commonDetail, sendProduct },
props: {
role: {
type: Object,
@ -103,13 +152,15 @@ export default {
},
data() {
return {
detailType: '1',//1: 2: 3:
sendProductVisible: false,
searchDate: [],
searchData: {
radioType: "1",
start_date: null,
end_date: null,
keyword: "",
audit_status: null,
audit_status: null, // this.role.audit_status
listing_status: null,
publish_type: "2",
manager_self: "",
@ -164,6 +215,8 @@ export default {
tableData: [],
total_count: 0,
loading: false,
showEditDialog: false,
editForm: {},
pickerOptions: {
shortcuts: [{
text: '最近一周',
@ -194,9 +247,84 @@ export default {
}
},
created() {
console.log("父组件传来的值是", this.role);
// created audit_status
if (this.role && this.role.audit_status) {
this.searchData.audit_status = this.role.audit_status;
}
if (this.role && this.role.publish_type) {
this.searchData.publish_type = this.role.publish_type;
}
this.getTableData();
},
watch: {
// role
role: {
handler(newRole) {
if (newRole && newRole.audit_status) {
this.searchData.audit_status = newRole.audit_status;
}
},
immediate: true,
deep: true
}
},
computed: {
showProductDetail: {
get() {
return this.$store.state.ncmatch.showProductDetail;
},
set(value) {
this.$store.commit('SHOWPRODUCTDETAIL', value);
}
}
},
methods: {
deleteProduct(row) {
console.log(row);
let ploay = {
id: row.id,
del_flg: '1'
}
reqEditProductNoImg(ploay).then(res => {
if (res.status) {
this.$message.success('删除成功~');
this.getTableData();
} else {
this.$message.error('删除失败~');
}
})
},
grounding(row, listing_status) {
console.log(listing_status);
let ploay = {
id: row.id,
listing_status: listing_status,
}
reqEditProductNoImg(ploay).then(res => {
let msg = listing_status == 'listing' ? '上架' : '下架';
if (res.status) {
this.$message.success(msg + '成功~');
this.getTableData();
} else {
this.$message.error(msg + '失败~');
}
})
},
sendProductSuccess() {
this.sendProductVisible = false; //
this.getTableData(); //
},
//
closeAddDialog() {
this.sendProductVisible = false;
},
openSendProduct(item) {
this.sendProductVisible = true;
this.sendProductData = item;
},
handleRadioChange(value) {
this.searchData.publish_type = this.radioMap[value].publish_type;
this.searchData.manager_self = this.radioMap[value].manager_self;
@ -208,10 +336,26 @@ export default {
this.searchDate = [];
this.searchData.keyword = "";
this.searchData.current_page = 1;
this.searchData.audit_status = this.role.audit_status;
this.getTableData();
},
exportData() {
this.$message.info('导出功能开发中...');
exportData(row) {
let ploay = {
// ids: [row.id]
ids:["fYHuG9eXwLi7949Rg9mRg", "vTv8KuqfDhT1efC8prs9y"]
}
reqExportProduct(ploay).then(res => {
console.log(res);
// 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');
})
},
handleDetailClose() {
//
@ -220,13 +364,35 @@ export default {
handleApprove() {
//
console.log('审核通过');
this.getTableData();
//
},
handleReject() {
//
console.log('审核不通过');
this.getTableData();
//
},
//
editProduct(item) {
console.log('编辑商品数据:', item);
console.log('公司类别数据:', item.company_type);
this.editForm = { ...item };
this.showEditDialog = true;
},
//
closeEditDialog() {
this.showEditDialog = false;
this.editForm = {};
},
//
handleEditSuccess() {
//
const successMessage = this.editForm.publish_type === '2' ? '需求编辑成功!' : '商品编辑成功!';
this.$message.success(successMessage);
this.closeEditDialog();
this.getTableData(); //
},
getTableData() {
//
if (this.searchDate && this.searchDate.length === 2) {
@ -257,37 +423,33 @@ export default {
console.log(`每页 ${val}`);
},
handleCurrentChange(val) {
this.searchData.current_page = val;
this.getTableData();
console.log(`当前页: ${val}`);
},
openDetail(item) {
openDetail(item, type) {
this.detailType = type;
this.loading = true;
reqGetProductDetail({
id: item.id,
from: 'b'
}).then(async res => {
this.loading = false
this.loading = false;
if (res.status) {
await this.$store.commit('SETPRODUCTDETAIL', res.data);
await this.$store.commit('SHOWPRODUCTDETAIL', true);
} else {
this.$message.error(res.msg);
this.$set(this.loadingStates, item.id, false);
this.$message.error(res.msg || '获取详情失败');
}
})
}
},
computed: {
showProductDetail: {
get() {
return this.$store.state.ncmatch.showProductDetail;
},
set(value) {
this.$store.commit('SHOWPRODUCTDETAIL', value);
}
}).catch(error => {
this.loading = false;
console.error('获取详情失败:', error);
this.$message.error('获取详情失败,请重试');
});
},
openAddDialog() {
this.publish_type = this.searchData.radioType === '1' || this.searchData.radioType === '3' ? '2' : '1';
this.sendProductVisible = true;
}
}
}
@ -322,7 +484,7 @@ export default {
.el-radio-button__inner {
border-radius: 4px;
margin-right: 8px;
border: 1px solid #dcdfe6;
border: 1px solid #dcdfe6;
background-color: #f5f7fa;
color: #606266;

View File

@ -1,23 +1,34 @@
<template>
<el-dialog
:title="dialogTitle"
top="5vh"
:visible.sync="visible"
width="80%"
@open="scrollToTop"
@close="handleClose">
<ProductDetail></ProductDetail>
<span slot="footer" class="dialog-footer">
<el-button @click="handleClose"> </el-button>
<el-button type="success" @click="handleApprove">审核通过</el-button>
<el-button type="danger" @click="handleReject">审核不通过</el-button>
</span>
</el-dialog>
<div>
<el-dialog :title="dialogTitle" top="5vh" :visible.sync="visible" width="80%" @open="scrollToTop"
@close="handleClose">
<ProductDetail :isApprove="true" ></ProductDetail>
<span slot="footer" class="dialog-footer">
<el-button @click="handleClose"> </el-button>
<el-button type="success" v-if="showType===2" @click="handleApprove">审核通过</el-button>
<el-button type="danger" v-if="showType===2" @click="handleReject">审核不通过</el-button>
</span>
</el-dialog>
<!-- Form -->
<el-dialog title="审核退回" :visible.sync="dialogFormVisible">
<el-form :model="form">
<el-form-item label="驳回原因" :label-width="formLabelWidth">
<el-input v-model="form.reject_reason" autocomplete="off" type="textarea" :rows="3"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false"> </el-button>
<el-button type="primary" @click="rejectProduct"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import ProductDetail from '@/views/homePage/ncmatch/proDetail/index.vue';
import { mapState } from 'vuex';
import { reqEditProductNoImg } from '@/api/ncmatch';
export default {
name: 'commonDetail',
@ -26,6 +37,19 @@ export default {
visible: {
type: Boolean,
default: false
},
showType: {
type: String,
default: '1'
}
},
data() {
return {
dialogFormVisible: false,
form: {
reject_reason: ''
},
formLabelWidth: '120px'
}
},
computed: {
@ -56,26 +80,56 @@ export default {
type: 'warning'
}).then(() => {
// API
this.$emit('approve');
this.$message.success('审核通过成功');
this.handleClose();
let ploay = {
id: this.productDetailInfo.id,
audit_status: "approved"
}
reqEditProductNoImg(ploay).then(res => {
if (res.status) {
this.$emit('approve');
this.$message.success('审核通过成功');
this.handleClose();
} else {
this.$message.error(res.msg || '审核失败,请重试')
}
})
}).catch(() => {
this.$message.info('已取消操作');
});
},
handleReject() {
this.$confirm('确认审核不通过吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// API
this.$emit('reject');
this.$message.success('审核不通过成功');
this.handleClose();
}).catch(() => {
this.$message.info('已取消操作');
});
this.dialogFormVisible = true;
// this.$confirm('', '', {
// confirmButtonText: '',
// cancelButtonText: '',
// type: 'warning'
// }).then(() => {
// // API
// this.$emit('reject');
// this.$message.success('');
// this.handleClose();
// }).catch(() => {
// this.$message.info('');
// });
},
rejectProduct() {
let ploay={
id:this.productDetailInfo.id,
audit_status:"rejected",
reject_reason:this.form.reject_reason
}
reqEditProductNoImg(ploay).then(res => {
this.dialogFormVisible=false;
if (res.status) {
this.$emit('reject');
this.handleClose();
this.$message.success('审核成功');
}else{
this.$message.error('审核失败,请重试');
}
})
}
}
}

View File

@ -14,12 +14,22 @@ export default {
return {
role:{
role_type:'customer',
type:'look'
audit_status:'',
publish_type:null
}
}
},
created() {
if(sessionStorage.getItem('jueseNew').includes('运营')){
console.log("运营");
this.role.role_type='customer'
}else if(sessionStorage.getItem('jueseNew').includes('客户')){
console.log("客户");
this.role.role_type='user'
}
console.log("role",this.role);
this.role.audit_status='pending,approved'
this.role.publish_type='1'
},
}

View File

@ -0,0 +1,37 @@
<template>
<div>
<commonBox :role="role"></commonBox>
</div>
</template>
<script>
//| |
import commonBox from '@/views/customer/productMangement/commonBox/index.vue';
export default {
name: 'productList',
components: { commonBox },
data() {
return {
role:{
role_type:null,
audit_status:null,
publish_type:null
}
}
},
created() {
if(sessionStorage.getItem('jueseNew').includes('运营')){
console.log("运营");
this.role.role_type='customer'
}else if(sessionStorage.getItem('jueseNew').includes('客户')){
console.log("客户");
this.role.role_type='user'
}
console.log("role",this.role);
this.role.audit_status='rejected'
this.role.publish_type='1'
},
}
</script>

View File

@ -1,7 +1,6 @@
<template>
<div style="width: 100%;">
<div
id="topContainer"
<div id="topContainer"
style="min-width: 1400px; width: 100%;display: flex;flex-direction: column;justify-content: flex-start;align-items: center">
<div class="banner">
<img src="./img/detailBg.jpg" alt="">
@ -13,81 +12,36 @@
</div>
<!-- 产品优势 -->
<div class="proAd">
<div v-show="detailData.title==='裸金属产品'" class="title">产品规格</div>
<div v-show="detailData.title === '裸金属产品'" class="title">产品规格</div>
<div v-show="detailData.title==='裸金属产品'" class="cardBox">
<div v-show="detailData.title === '裸金属产品'" class="cardBox">
<div class="card">
<ul class="cardUl">
<li class="cardLi">
<span class="title"> {{ data4090.title }}</span>
<!-- ::{{ JSON.stringify(detailData) }} -->
<li class="cardLi" v-for="(i, index) in detailData.products" :key="index">
<span class="title"> {{ i.title }}</span>
<ul class="tagUl">
<li v-for="(item,index) in data4090.list" :key="index">
<li v-for="(item,index) in i.list" :key="index">
{{ item.name }}{{ item.content }}
</li>
</ul>
<div class="towBtn">
<span class="basePrice"><span style="font-weight: bold;"></span><span
style="font-weight: bold;font-size: 48px">{{ data4090.price }}</span> <span
style="color: #7A82A0">/{{ data910B.price_unit }}</span> <span
style="color: #222F60">可短租</span></span>
style="font-weight: bold;font-size: 48px">{{ i.price }}</span> <span
style="color: #7A82A0">/{{ i.price_unit }}</span><span style="color: #222F60"></span></span>
<span @click="openTalk" class="zixun">立即咨询</span>
</div>
</li>
<li class="cardLi">
<span class="title"> {{ dataA100.title }}</span>
<ul class="tagUl">
<li v-for="(item,index) in dataA100.list" :key="index">
{{ item.name }}{{ item.content }}
</li>
</ul>
<div class="towBtn">
<span class="basePrice"><span style="font-weight: bold;"></span><span
style="font-weight: bold;font-size: 48px">{{ dataA100.price }}</span> <span
style="color: #7A82A0">/{{ data910B.price_unit }}</span><span style="color: #222F60"></span></span>
<span @click="openTalk" class="zixun">立即咨询</span>
</div>
</li>
<li class="cardLi">
<span class="title"> {{ dataA800.title }}</span>
<ul class="tagUl">
<li v-for="(item,index) in dataA800.list" :key="index">
{{ item.name }}{{ item.content }}
</li>
</ul>
<div class="towBtn">
<span class="basePrice"><span style="font-weight: bold;"></span><span
style="font-weight: bold;font-size: 48px">{{ dataA800.price }}</span> <span
style="color: #7A82A0">/{{ data910B.price_unit }}</span> <span
style="color: #222F60">可短租</span></span>
<span @click="openTalk" class="zixun">立即咨询</span>
</div>
</li>
<li class="cardLi">
<span class="title"> {{ data910B.title }}</span>
<ul class="tagUl">
<li v-for="(item,index) in data910B.list" :key="index">
{{ item.name }}{{ item.content }}
</li>
</ul>
<div class="towBtn">
<span class="basePrice"><span style="font-weight: bold;"></span><span
style="font-weight: bold;font-size: 48px">{{ data910B.price }}</span> <span
style="color: #7A82A0">/{{ data910B.price_unit }}</span> <span
style="color: #222F60">可短租</span></span>
<span @click="openTalk" class="zixun">立即咨询</span>
</div>
</li>
</ul>
</div>
</div>
<div style="padding-top: 50px" v-if="detailData.title!=='裸金属产品'" class="title">产品优势</div>
<ul v-if="detailData.title!=='裸金属产品'" class="adUl">
<div style="padding-top: 50px" v-if="detailData.title !== '裸金属产品'" class="title">产品优势</div>
<ul v-if="detailData.title !== '裸金属产品'" class="adUl">
<li v-for="(item, index) in detailData.adList" :key="index">
<div class="leftBox">
<img :src="item.img" alt="">
@ -100,39 +54,36 @@
</ul>
</div>
<!-- 产品功能-->
<div v-if="detailData.title!=='裸金属产品'" style="justify-content: flex-start!important;" class="proUse">
<div v-if="detailData.title !== '裸金属产品'" style="justify-content: flex-start!important;" class="proUse">
<div style="padding-top: 50px" class="title">产品功能</div>
<ul class="useUl">
<li v-for="(item,index) in detailData.useList" :key="index" class="useLi">
<span v-if=" item.title" class="useTitle">{{ item.title }}</span>
<span v-if=" item.description" class="useDescription">{{ item.description }}</span>
<li v-for="(item, index) in detailData.useList" :key="index" class="useLi">
<span v-if="item.title" class="useTitle">{{ item.title }}</span>
<span v-if="item.description" class="useDescription">{{ item.description }}</span>
<ul class="inUl">
<li :style="{ justifyContent: i.description ? 'space-around' : 'center' }" v-for="(i,d) in item.content"
:key="d"
class="inLi">
<li :style="{ justifyContent: i.description ? 'space-around' : 'center' }" v-for="(i, d) in item.content"
:key="d" class="inLi">
<span class="inTitle" style="margin-bottom: 10px;font-size: 16px;">{{ i.title }}</span>
<span style="color: #666;line-height: 1.8;font-size: 14px" class="inDescription">{{
i.description
}}</span>
i.description
}}</span>
</li>
</ul>
</li>
</ul>
</div>
<!-- Application-->
<div v-if="detailData.title!=='裸金属产品'" class="application">
<div v-if="detailData.title !== '裸金属产品'" class="application">
<div class="title">应用场景</div>
<ul class="menu">
<li v-for="(item,index) in detailData.applicationList" :key="index" @click="clickMenu(item)"
:class="activeMenu===item.id?'activeLi':''">
<li v-for="(item, index) in detailData.applicationList" :key="index" @click="clickMenu(item)"
:class="activeMenu === item.id ? 'activeLi' : ''">
{{ item.memuName }}
</li>
</ul>
<div class="realApp">
<div style="display: none" class="leftBox">
<img
:src="menuObj.img"
alt="">
<img :src="menuObj.img" alt="">
</div>
<div class="rightBox">
<div v-if="menuObj.title_r" class="topBox">
@ -141,8 +92,7 @@
</div>
<div class="boxBox">
<div v-for="(item,index) in menuObj.provide"
class="bottomBox">
<div v-for="(item, index) in menuObj.provide" class="bottomBox">
<span style="margin: 15px 0">{{ item.title }}</span>
<span class="content">{{ item.content }}</span>
</div>
@ -158,9 +108,9 @@
<script>
import Vue from 'vue'
// import {detailData} from "@/views/homePage/detail/data";
import {reqHotProduct, reqNewHomeDetail} from "@/api/newHome";
import { reqHotProduct, reqNewHomeDetail } from "@/api/newHome";
import Talk from "@/views/homePage/dialog/talk/index.vue";
import {luojinshuData} from "@/views/homePage/detail/luojinshu";
export default Vue.extend({
name: "detail",
@ -169,7 +119,7 @@ export default Vue.extend({
// return luojinshuData
// }
// },
components: {Talk},
components: { Talk },
// computed: {
// detailData() {
// return detailData
@ -177,11 +127,6 @@ export default Vue.extend({
// },
data() {
return {
data4090: luojinshuData["4090"],
dataA100: luojinshuData["A100"],
dataA800: luojinshuData["A800"],
data910B: luojinshuData["910B"],
loading: null,
activeMenu: 0,
detailData: {},
@ -243,7 +188,7 @@ export default Vue.extend({
},
getDetail() {
this.openFullScreen2()
reqNewHomeDetail({id: this.$route.query.id}).then(res => {
reqNewHomeDetail({ id: this.$route.query.id }).then(res => {
this.loading.close()
this.detailData = res.data
this.activeMenu = res.data.applicationList[0].id

View File

@ -1,237 +1,2 @@
export const luojinshuData = {
"hot": {
"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"
}
]
},
"4090": {
"title": "NVIDIA-4090",
"description": "轻量应用服务器Light server是一种内置应用型镜像或系统型镜像的小规格云服务器 绑定适配大小的磁盘和带宽为中小型企业和个人用户提供官网搭建、web应用搭建、云上学习和测试等场景的服务。",
"price": "6000",
"pre_price": "7143",
"price_unit": "台/月",
"discount": 8.4,
"bgImgUrl": "https://www.kaiyuancloud.cn/idfile?path=firstpagebg/2.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"
}
]
},
"A100": {
"title": "NVIDIA-A100",
"description": "对象存储BOSBaidu Object Storage是一款稳定、安全、高效、高可拓展的云存储服务支持标准、低频、冷和归档存储等多种存储类型满足多场景的存储需求。用户可以将任意数量和形式的非结构化数据存入BOS并对数据进行管理和处理。",
"price": "31000",
"pre_price": null,
"price_unit": "台/月",
"discount": null,
"bgImgUrl": "https://www.kaiyuancloud.cn/idfile?path=firstpagebg/3.png",
"list": [
{
"id": 1,
"name": "CPU",
"content": "AMD EPYC 7543 32 C * 2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssc2x.png"
},
{
"id": 2,
"name": "内存",
"content": "64G DDR4-3200 * 32",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/cpu2x.png"
},
{
"id": 3,
"name": "GPU",
"content": "NVIDIA-A100-80GB-Nvlink * 8",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/sdcard2x.png"
},
{
"id": 4,
"name": "系统盘",
"content": "960G SATA SSD * 2",
"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": "MCX653105A - HDAT CX6 200G 1P HDR * 4",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssdr2x.png"
}
]
},
"A800": {
"title": "NVIDIA-A800",
"description": "云数据库 RDS为用户提供高性价比、安全可靠、性能卓越、易于维护的企业级云数据库服务。",
"price": "32000",
"pre_price": null,
"price_unit": "台/月",
"discount": null,
"bgImgUrl": "https://www.kaiyuancloud.cn/idfile?path=firstpagebg/4.png",
"list": [
{
"id": 1,
"name": "CPU",
"content": "Intel (R) Xeon (R) Platinum 8358 32 C * 2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssc2x.png"
},
{
"id": 2,
"name": "内存",
"content": "64G DDR4-3200 * 32",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/cpu2x.png"
},
{
"id": 3,
"name": "GPU",
"content": "NVIDIA-A800-80GB-Nvlink * 8",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/sdcard2x.png"
},
{
"id": 4,
"name": "系统盘",
"content": "960G SATA SSD * 2",
"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": "MCX653105A - HDAT CX6 200G 1P HDR *4",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssdr2x.png"
}
]
},
"910B": {
"title": "昇腾-910B",
"description": "裸金属服务器是一种融合了高性能 GPU 计算能力、裸金属服务器物理资源独占性及云计算弹性扩展特性的新型服务器形态。它既具备传统裸金属服务器的强劲性能与安全性,又能像云服务器一样按需灵活调整资源,适用于对算力、资源独占性和弹性要求极高的场景。",
"price": "22000",
"pre_price": "27500",
"price_unit": "台/月",
"discount": 8,
"bgImgUrl": "https://www.kaiyuancloud.cn/idfile?path=firstpagebg/5.png",
"list": [
{
"id": 23,
"name": "CPU",
"content": "鲲鹏920 * 4",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssc2x.png"
},
{
"id": 24,
"name": "内存",
"content": "32G DDR4-3200 * 32",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/cpu2x.png"
},
{
"id": 25,
"name": "GPU",
"content": "昇腾-910B-64GB * 8",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/sdcard2x.png"
},
{
"id": 26,
"name": "系统盘",
"content": "480G SATA SSD * 2",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/servermini2x.png"
},
{
"id": 27,
"name": "数据盘",
"content": "3.84T U.2 NVMe SSD * 1",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/serverpath2x.png"
},
{
"id": 28,
"name": "网卡",
"content": "200G QSFP ROCE * 8",
"icon": "https://www.kaiyuancloud.cn/idfile?path=firstpagehot/ssdr2x.png"
}
]
}
}
export const luo=[]

View File

@ -4,7 +4,7 @@
<div class="search-box">
<search></search>
</div>
<div style="width: 90%;max-width: 1600px;border:1px solid red;">
<div style="width: 90%;max-width: 1600px;">
<router-view></router-view>
</div>
<el-dialog :title="productDetailInfo.publish_type==='1'? '商品详情' :'需求详情'" top="5vh" :visible.sync="showProductDetail" width="80%" @open="scrollToTop">
@ -173,8 +173,7 @@ export default Vue.extend({
<style scoped lang="scss">
.search-box {
width: 100%;
margin-top: 55px;
border: 1px solid red;
margin-top: 15px;
display: flex;
flex-direction: column;
align-items: center;

View File

@ -1,7 +1,7 @@
<script lang="ts">
<script >
import Vue from 'vue'
import { reqPublishProductSearchFirstPage } from '@/api/ncmatch'
import {mapGetters, mapState} from "vuex";
import { mapGetters, mapState } from "vuex";
export default Vue.extend({
name: "mainPage",
components: {
@ -15,6 +15,7 @@ export default Vue.extend({
},
data() {
return {
total: 0,
publish_type: null,
sendProductVisible: false,
currentHotMenu: "",
@ -142,6 +143,10 @@ export default Vue.extend({
},
methods: {
handleCurrentChange(val) {
this.current_page = val
this.init_product_list()
},
sendInfo(type) {
if (this.loginState) {
this.publish_type = type
@ -157,6 +162,7 @@ export default Vue.extend({
this.currentHotMenu = res.data[0].id
this.hotProductList = res.data[0].product_list
this.product = res.data
this.total = res.data[0].total_count
}
}
@ -167,6 +173,7 @@ export default Vue.extend({
this.init_product_list()
},
clickNetMenu(menu) {
this.current_page = 1
this.currentHotMenu = menu.id;
this.hotProductList = menu.product_list || [];
},
@ -181,20 +188,12 @@ export default Vue.extend({
<template>
<div class="jd-homepage">
<!-- 顶部Header -->
<header class="header">
<!-- <header class="header">
<div class="header-content">
<!-- 左侧Logo -->
<!-- <div class="logo-section"> -->
<!--
<div class="logo">
<img src="./img/logo.png" alt="">
</div> -->
<!-- </div> -->
</div>
</header>
</header> -->
<!-- 主内容区域 -->
<main class="main-content">
<div class="content-wrapper">
@ -220,7 +219,7 @@ export default Vue.extend({
</section>
<!-- 右侧用户信息栏 -->
<aside class="user-sidebar" style="background-color: #f8fbfe;padding: 10px;padding-top: 20px;">
<aside class="user-sidebar" style="background-color: #f8fbfe;padding: 10px;padding-top: 20px;">
<span class="publish-goods" @click="$router.push('/ncmatchHome/supplyAndDemandSquare')"> 算力供需广场</span>
<span class="publish-goods" @click="sendInfo('2')">发布需求</span>
<span class="publish-goods" @click="sendInfo('1')">发布商品</span>
@ -244,9 +243,13 @@ export default Vue.extend({
<productCard :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>
<el-dialog :title="publish_type === '2' ? '发布需求' : '发布商品'" width="60vw" top="5vh"
:visible.sync="sendProductVisible">
<sendProduct v-if="publish_type" @success="sendProductSuccess" :publish_type="publish_type"></sendProduct>
<sendProduct :isEdit="false" v-if="publish_type" @success="sendProductSuccess" :publish_type="publish_type">
</sendProduct>
</el-dialog>
</div>
</div>
@ -291,7 +294,7 @@ export default Vue.extend({
width: 100%;
max-width: 1600px;
margin: 0 auto;
padding-bottom: 45px;
// padding-bottom: 45px;
}
.publish-goods {
@ -435,7 +438,6 @@ export default Vue.extend({
justify-content: flex-start;
align-items: center;
position: relative;
height: 100px;
.header-content {
width: 100%;
@ -506,7 +508,7 @@ export default Vue.extend({
margin: 20px auto;
margin-top: 10px;
border-radius: 10px;
overflow: hidden;
overflow: visible;
background-color: white;
height: 360px;

View File

@ -1,12 +1,20 @@
export function buildDynamicStructure(data, parentId = "None", currentLevel = 1) {
export function buildDynamicStructure(data, parentId = null, currentLevel = 1) {
// 1. 找出当前层级的节点
const currentNodes = data.filter(item => item.parentid === parentId);
if (currentNodes.length === 0) return [];
// 2. 处理每个节点
return currentNodes.map(node => {
return currentNodes.map((node, index) => {
const resultNode = {};
// 为每个节点添加唯一id
if (node.id) {
resultNode.id = node.id;
} else {
// 如果没有id生成一个唯一的id
resultNode.id = `level${currentLevel}_${parentId || 'root'}_${index}_${Date.now()}`;
}
// 设置层级名称字段
if (currentLevel === 1) {
resultNode.first_level_name = node.name;
@ -15,6 +23,9 @@ export function buildDynamicStructure(data, parentId = "None", currentLevel = 1)
resultNode.second_level_name = node.name;
} else if (currentLevel === 3) {
resultNode.third_level_name = node.name;
} else if (currentLevel === 4) {
// 第四级节点也需要保存name字段
resultNode.name = node.name;
}
// 3. 递归处理子节点
@ -27,9 +38,12 @@ export function buildDynamicStructure(data, parentId = "None", currentLevel = 1)
} else if (currentLevel === 2) {
resultNode.thirdClassification = children;
} else if (currentLevel === 3) {
// 第四级特殊处理为product_list
resultNode.product_list = children.map(child => ({
first_level_name: child.third_level_name || child.name
id: child.id,
first_level_name: child.name // 修复:使用 child.name 而不是 child.third_level_name
}));
}
}

View File

@ -4,14 +4,14 @@
<ul class="category-list">
<li class="category-item" style="color: #E02020;"><img src="../img/hot.svg" style="margin-right: 10px;"
alt=""> 热门推荐 / 活动促销</li>
<li v-for="category in categories" :key="category.first_level_name" class="category-item"
<li v-for="category in categories" :key="category.id" class="category-item"
@mouseenter="showProductList(category)" @mouseleave="hideProductList">
<span class="category-icon"> <img style="width: 24px;height: 24px;" :src="category.icon" alt=""> </span>
<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.second_level_name">
:key="secondary.id">
{{ secondary.second_level_name }}{{ index < category.secondaryClassification.length - 1 ? ' / '
: '' }} </span>
</div>
@ -19,12 +19,12 @@
</ul>
<transition name="slide-fade">
<!-- v-if="currentCategory" -->
<div 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.second_level_name" class="secondary-item"
:key="secondary.id" class="secondary-item"
:class="{ active: selectedSecondary === secondary }"
@mouseenter="selectSecondary(secondary)">
{{ secondary.second_level_name }}
@ -36,7 +36,7 @@
<!-- 如果有选中的二级菜单且有三级菜单显示京东风格的分类区域 -->
<div v-if="selectedSecondary && selectedSecondary.thirdClassification && selectedSecondary.thirdClassification.length > 0"
class="jd-style-menu">
<div v-for="third in selectedSecondary.thirdClassification" :key="third.third_level_name"
<div v-for="third in selectedSecondary.thirdClassification" :key="third.id"
class="category-section">
<!-- 只有当有四级菜单时才显示三级菜单标题 -->
<div v-if="third.product_list && third.product_list.length > 0" class="section-header">
@ -48,7 +48,7 @@
<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.first_level_name" class="product-tag">
:key="product.id" class="product-tag">
{{ product.first_level_name }}
</div>
</div>
@ -82,7 +82,7 @@
<!-- 默认显示所有二级菜单项 -->
<div v-else class="jd-style-menu">
<div v-for="secondary in currentCategory.secondaryClassification"
:key="secondary.second_level_name" class="category-section">
:key="secondary.id" class="category-section">
<div class="section-header">
<span class="section-title">{{ secondary.second_level_name }}</span>
<span class="section-arrow">></span>
@ -112,61 +112,8 @@ export default {
currentCategory: null,
selectedSecondary: null,
hideTimer: null,
categories: [
{
first_level_name: '云', icon: require('../img/cloud.png'), secondaryClassification: [
{
second_level_name: '百度云',
thirdClassification: [
{
third_level_name: '计算',
product_list: [{ first_level_name: 'ECS1' }, { first_level_name: 'ECS2' }, { first_level_name: 'ECS3' }]
},
{
third_level_name: '存储',
product_list: [{ first_level_name: 'BOS1' }, { first_level_name: 'BOS2' }]
}
]
},
{
second_level_name: '阿里云',
thirdClassification: [
{
third_level_name: '计算',
product_list: [{ first_level_name: 'ECS-A' }, { first_level_name: 'ECS-B' }]
}
]
}
]
},
{
first_level_name: '国产算力', icon: require('../img/cloud.png'), secondaryClassification: [
{
second_level_name: '昇腾910B',
thirdClassification: [
{
third_level_name: '昇腾910B1',
},
{
third_level_name: '昇腾910B2',
}
]
},
{
second_level_name: '昆仑芯',
thirdClassification: [
{
third_level_name: '昆仑芯A',
},
{
third_level_name: '昆仑芯B',
}
]
}
]
}
],
categories: [ ],
loading: false,
}
},
created() {
@ -174,9 +121,13 @@ export default {
},
methods: {
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);
// this.categories = res.data.map(item => {
// return {
// name: item.name,
@ -236,6 +187,7 @@ export default {
//
.category-sidebar {
position: relative;
background-color: #f8fbfe;
height: 100%;
border-radius: 10px;
@ -306,14 +258,17 @@ export default {
position: absolute;
left: 100%;
top: 0;
width: 900px;
border: 1px solid red;
width: 1000px;
background: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 1000;
z-index: 9999;
padding: 25px;
min-height: 400px;
// height: 500px;
margin-left: 10px;
overflow: visible;
white-space: nowrap;
/* 添加一个透明的连接区域,防止鼠标移动时意外消失 */
&::before {
@ -327,7 +282,10 @@ export default {
}
.rightBox-content {
.secondary-menu {
line-height: 1.5;
display: flex;
flex-wrap: wrap;
@ -355,6 +313,10 @@ export default {
.menu-content {
min-height: 200px;
height: 100%;
max-height: calc(100vh - 280px);
overflow-y: auto;
border: 5px solid red;
.jd-style-menu {
width: 100%;

View File

@ -1,5 +1,8 @@
<template>
<div class="form-container">
<div v-if="form.reject_reason" class="tip">
<i class="el-icon-error"></i>未通过原因: <span style="color: #666;font-size: 12px;">{{ form.reject_reason }}</span>
</div>
<el-form :model="form" :rules="rules" ref="form" label-width="120px" class="two-column-form">
<!-- 商品图片 - 单独一行 -->
<el-form-item v-if="publish_type === '1'" :label="publish_type === '2' ? '需求图片' : '商品图片'" prop="img"
@ -72,7 +75,7 @@
</el-form-item>
</div>
<div class="form-row">
<el-form-item label="GPU支持" prop="cart_flag" required class="form-item-half">
<el-form-item label="GPU支持" prop="cart_flag" required class="form-item-half">
<el-radio-group v-model="form.cart_flag">
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
@ -124,18 +127,34 @@
</el-form-item>
</div>
<div class="form-row">
<el-form-item label="折扣" class="full-width">
<el-input-number :controls="false" v-model="form.discount" :precision="2" :step="0.01" :max="10">
<el-form-item label="折扣" class="full-width">
<el-input-number style="width: 120px;" :controls="false" v-model="form.discount" :precision="2"
:step="0.01" :max="10">
</el-input-number>
<span style="margin-left: 10px; font-weight: bold;"> </span>
<span style="margin-left: 10px; font-weight: bold;"> </span>
<span style="margin-left: 10px; font-weight: bold;font-size: 14px;">折后价:
<span v-if="form.discount !== 0 && form.price" style="color: red;">{{ (form.price *
form.discount/10).toFixed(2 ) }}</span>
<span v-else>-</span>
</span>
<!-- <el-input v-model="form.discount" placeholder="请输入折扣" type="number">
<template slot="append"></template>
</el-input> -->
</el-input> -->
</el-form-item>
<el-form-item class="full-width">
<template slot="label">
<span>平台服务费用</span>
<el-tooltip content="平台服务费用为商品价格的3%,如有疑问请联系客服进行处理。" placement="top" effect="light">
<i class="el-icon-warning-outline"
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-else>-</span>
</el-form-item>
<!-- <el-form-item label="折扣后价格" class="full-width">
<span>{{ form.price * form.discount }}</span>
</el-form-item> -->
</div>
<el-form-item :label="publish_type === '2' ? '需求概述' : '商品概述'" prop="requirement_summary" required
@ -155,9 +174,11 @@
<!-- 提交按钮 -->
<div class="form-actions">
<el-button type="primary" @click="submitForm" size="large">{{ publish_type === '2' ? '发布需求' : '发布商品'
}}</el-button>
<el-button type="primary" @click="submitForm" size="large">
{{ isEdit ? (publish_type === '2' ? '保存需求' : '保存商品') : (publish_type === '2' ? '发布需求' : '发布商品') }}
</el-button>
<el-button @click="resetForm" size="large">重置</el-button>
<el-button @click="handleClose" size="large">关闭</el-button>
<!-- <el-button type="info" @click="getBinaryData" size="large">获取二进制数据</el-button> -->
</div>
</el-form>
@ -193,13 +214,21 @@
<script>
import { VueCropper } from 'vue-cropper'
import { buildCaTree } from './buildCaTree'
import { reqGetProductCategorySearch, reqPublishProductAdd, reqCompanyCategorySearch } from '@/api/ncmatch'
import { reqGetProductCategorySearch, reqPublishProductAdd, reqCompanyCategorySearch, reqEditProduct } from '@/api/ncmatch'
export default {
name: 'sendProduct',
props: {
publish_type: {
type: String,
default: '1' // 'product'
},
isEdit: {
type: Boolean,
default: false
},
editData: {
type: Object,
default: () => ({})
}
},
components: {
@ -245,8 +274,9 @@ export default {
application_scenario: "",//
unit: "",//
short_term: "",//
discount:null,//
discount: null,//
cart_flag: '1',//GPU
reject_reason: "",//
},
rules: {
product_name: [
@ -320,10 +350,75 @@ export default {
created() {
this.init_product_category()
this.init_company_category()
//
if (this.isEdit && this.editData && Object.keys(this.editData).length > 0) {
Promise.all([
this.init_product_category(),
this.init_company_category()
]).then(() => {
this.$nextTick(() => {
this.fillEditData(this.editData);
});
});
}
},
watch: {
editData: {
handler(newVal) {
if (newVal && Object.keys(newVal).length > 0 && this.isEdit) {
//
this.$nextTick(() => {
this.fillEditData(newVal);
});
}
},
immediate: false, // false
deep: true
}
},
methods: {
//
fillEditData(data) {
//
if (data.img) {
this.selectedImage = data.img;
this.form.img = data.img;
}
//
Object.keys(this.form).forEach(key => {
if (data[key] !== undefined && data[key] !== null) {
this.form[key] = data[key];
}
});
//
if (data.company_type) {
//
if (Array.isArray(data.company_type)) {
this.form.company_type = [...data.company_type];
} else if (typeof data.company_type === 'string') {
//
if (data.company_type.includes(',')) {
this.form.company_type = data.company_type.split(',').map(item => item.trim());
} else {
this.form.company_type = [data.company_type];
}
}
}
if (data.product_category && Array.isArray(data.product_category)) {
this.form.product_category = [...data.product_category];
}
console.log('编辑数据已填充:', this.form);
console.log('公司类别数据:', data.company_type);
console.log('处理后的公司类别:', this.form.company_type);
},
init_company_category() {
reqCompanyCategorySearch({ url_link: window.location.href }).then(res => {
return reqCompanyCategorySearch({ url_link: window.location.href }).then(res => {
if (res.status) {
this.company_category_list = []
for (let item of res.data) {
@ -332,8 +427,8 @@ export default {
value: item.company_category
})
}
}
return res;
})
},
// base64Blob
@ -349,11 +444,11 @@ export default {
init_product_category() {
// publish_type to_page
reqGetProductCategorySearch({ url_link: window.location.href, to_page: 'publish' }).then(res => {
return reqGetProductCategorySearch({ url_link: window.location.href, to_page: 'publish' }).then(res => {
if (res.status) {
this.typeList = buildCaTree(res.data)
}
return res;
})
},
//
@ -493,22 +588,49 @@ export default {
if (valid) {
console.log('表单数据:', this.form)
console.log('发布类型:', this.publish_type)
console.log('是否为编辑模式:', this.isEdit)
let formdata = new FormData();
for (let key in this.form) {
formdata.append(key, this.form[key]);
}
formdata.append('publish_type', this.publish_type)
//
// formdata.append('publish_type', this.publish_type);
reqPublishProductAdd(formdata).then(res => {
if (res.status) {
this.$emit('success');
const successMessage = this.publish_type === '2' ? '添加需求成功!' : '添加产品成功!'
this.$message.success(successMessage)
}
})
//
if (this.isEdit) {
formdata.append('id', this.editData.id || '');
reqEditProduct(formdata).then(res => {
if (res.status) {
this.$emit('success');
//
// const successMessage = this.publish_type === '2' ? '' : ''
// this.$message.success(successMessage)
} else {
this.$message.error(res.msg || '编辑失败,请重试')
}
}).catch(error => {
console.error('编辑请求失败:', error);
this.$message.error('编辑失败,请检查网络连接后重试')
})
} else {
reqPublishProductAdd(formdata).then(res => {
if (res.status) {
this.$emit('success');
const successMessage = this.publish_type === '2' ? '添加需求成功!' : '添加产品成功!'
this.$message.success(successMessage)
} else {
this.$message.error(res.msg || '添加失败,请重试')
}
}).catch(error => {
loading.close();
console.error('添加请求失败:', error);
this.$message.error('添加失败,请检查网络连接后重试')
})
}
} else {
this.$message.error('请完善表单信息')
}
@ -521,10 +643,51 @@ export default {
if (this.selectedImage) {
URL.revokeObjectURL(this.selectedImage)
}
//
this.$refs.form.resetFields()
this.selectedImage = null
this.form.img = null
this.$refs.fileInput.value = ''
//
if (this.isEdit && this.editData && Object.keys(this.editData).length > 0) {
this.$nextTick(() => {
this.fillEditData(this.editData);
});
} else {
//
this.selectedImage = null
this.form.img = null
this.$refs.fileInput.value = ''
//
this.form = {
url_link: window.location.href,
img: null,
product_name: "",
product_category: "",
company_name: "",
company_type: [],
contact_person: "",
job_title: "",
phone_number: "",
email: "",
cpu: "",
memory: "",
gpu: "",
sys_disk: "",
data_disk: "",
net_card: "",
priority: "",
price: "",
unit: "",
short_term: "",
label: "",
requirement_summary: "",
related_parameters: "",
application_scenario: "",
discount: null,
cart_flag: '1',
}
}
},
//
@ -577,6 +740,9 @@ export default {
console.log('没有二进制数据')
return null
}
},
handleClose() {
this.$emit('close');
}
}
}
@ -983,4 +1149,25 @@ export default {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwgAADsIBFShKgAAAABl0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC4xOdTWsmQAAAGiSURBVDhPY2AYBYMRMDIyMjAyMjL8//+f4f///wz//v1j+Pv3L8OfP38Yfv/+zfDnzx+G37//MPz69Yvh58+fDD9+/GD4/v07w7dv3xi+fv3K8OXLF4bPnz8zfPr0ieHjx48MHz58YHj//j3Du3fvGN6+fcvw5s0bhtevXzO8evWK4eXLlwwvXrxgeP78OcOzZ88Ynj59yvDkyROGx48fMzx69Ijh4cOHDA8ePGC4f/8+w7179xju3r3LcOfOHYbbt28z3Lp1i+HmzZsMN27cYLh+/TrDtWvXGK5evcpw5coVhsuXLzNcunSJ4eLFiwwXLlxgOH/+PMO5c+cYzp49y3DmzBmG06dPM5w6dYrh5MmTDCdOnGA4fvw4w7FjxxiOHj3KcOTIEYbDhw8zHDp0iOHgwYMMBw4cYNi/fz/Dvn37GPbu3cuwZ88eht27dzPs2rWLYefOnQw7duxg2L59O8O2bdsYtm7dyrBlyxaGzZs3M2zatIlh48aNDBs2bGBYv349w7p16xjWrl3LsGbNGoZVq1YxrFy5kmHFihUMy5cvZ1i2bBnD0qVLGZYsWcKwePFihkWLFjEsXLiQYcGCBQzz59nmDdvHsPcuXMZ5syZwzB79myGWbNmMcycOZNhxowZDNOnT2eYNm0aw9SpUxmmTJnCMHnyZIZJkyYxTJw4kWHChAkM48ePZxg3bhzD2LFjGcaMGcMwevRohlGjRjGMHDmSYcSIEQzDhw9nGDZsGMPQoUMZhgwZwjB48GCGQYMG0Q8AABKxLhX5TqgFAAAAAElFTkSuQmCC');
}
}
.tip{
font-size: 16px;
font-weight: bold;
color: red;
padding: 10px;
border-radius: 5px;
background-color: #ecf7fe;
position: relative;
overflow: hidden;
margin-bottom: 20px;
&::after{
content: '';
position: absolute;
top: 0;
left: 2px;
transform: translateX(-50%);
width: 5px;
height: 100%;
background-color: rgb(189, 83, 83);
}
}
</style>

View File

@ -5,7 +5,7 @@
{{ productDetailInfo.product_name }}
</h1>
<button class="consult-btn" @click="openTalk">立即咨询</button>
<button v-if="!isApprove" class="consult-btn" @click="openTalk">立即咨询</button>
</div>
<!-- 标题区域 -->
<ul class="title-section">
@ -139,6 +139,12 @@
import { mapState } from "vuex";
export default {
name: 'proDetail',
props: {
isApprove: {
type: Boolean,
default: false
}
},
data() {
return {

View File

@ -1,41 +1,70 @@
<template>
<div
style="display: flex;align-items: center;justify-content: center;border:1px solid red;width: 100%;max-width: 1400px;">
style="display: flex;align-items: center;justify-content: center;width: 100%;max-width: 1400px;">
<img style="width: 180px;height: 60px;padding-right: 20px;" src="https://www.kaiyuancloud.cn/idfile?path=logo_ncmatch.png" alt="">
<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;">
<!-- 中间搜索区域 -->
<div class="search-section" style="position: relative;margin: 0 20px;">
<div class="search-bar">
<input v-model="searchKeyword" type="text" class="search-input" placeholder="搜你想搜...">
<div class="search-bar" :class="{ 'has-results': showSearchResults && searchResults.length > 0 }">
<el-select class="mySelect" v-model="publish_type" placeholder="请选择" @change="handleTypeChange"
style="width: 75px;">
<el-option label="商品" value="1">
</el-option>
<el-option label="需求" value="2"></el-option>
</el-select>
<input v-model="keyword" type="text" class="search-input" placeholder="搜你想搜..." @input="handleInputChange"
@focus="handleInputFocus" @blur="handleInputBlur">
<button class="search-btn" @click="handleSearch">搜索</button>
</div>
<!-- 实时搜索结果 -->
<div v-if="showSearchResults && searchResults.length > 0" class="search-results" @click.stop>
<div v-for="result in searchResults" :key="result.id" class="search-result-item"
@click.stop.prevent="handleSearch(result)"
@mousedown.stop.prevent
@mouseup.stop.prevent
style="cursor: pointer; padding: 10px">
<span class="result-title" v-html="highlightKeyword(result)"></span>
</div>
</div>
<!-- 热搜关键词 -->
<div class="hot-search">
<a v-for="keyword in hotSearchKeywords" :key="keyword" href="#" class="hot-keyword">
<a v-for="keyword in hotSearchKeywords" :key="keyword" href="#" class="hot-keyword"
@click="handleHotKeywordClick(keyword)">
{{ keyword }}
</a>
</div>
</div>
<!-- position: absolute;right: -150px;top: 0px; -->
<span
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>
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>
</div>
</div>
</template>
<script>
import eventBus from '@/utils/eventBus'
import { reqSearch } from '@/api/ncmatch'
export default {
name: 'search',
data() {
return {
keyword: '',
publish_type: '1',
showSearchResults: false,
searchResults: [],
searchTimeout: null,
//
//
hotSearchKeywords: [
'昆仑芯-P800',
'天垓-150',
@ -86,15 +115,453 @@ export default {
}
],
}
},
//
// watch
methods: {
goHome(event) {
if (this.$route.path != '/ncmatchHome/index') {
console.log('准备跳转到:', '/ncmatchHome/index');
this.$router.push('/ncmatchHome/index').then(() => {
console.log('跳转成功');
}).catch(err => {
console.error('跳转失败:', err);
});
} else {
console.log('已经在首页,无需跳转');
}
},
goSearch() {
//
const currentRoute = this.$route;
const currentDisplayPage = currentRoute.query.display_page;
const targetQuery = {
keyword: this.keyword,
publish_type: this.publish_type,
display_page: 'list', // 使 list
current_page: 1,
page_size: 8
};
console.log('准备跳转到搜索结果页面:', { currentRoute, targetQuery });
// display_page
if (currentDisplayPage !== 'list') {
//
console.log('执行路由跳转');
this.$router.push({
path: '/ncmatchHome/search',
query: targetQuery
}).catch(err => {
//
if (err.name !== 'NavigationDuplicated') {
console.error('路由导航错误:', err);
} else {
console.log('路由重复导航,忽略错误');
}
});
} else {
//
console.log('已在搜索结果页面,重新调用接口获取最新数据');
this.performFormalSearch(this.keyword, this.publish_type);
}
},
//
handleInputChange() {
//
if (this.searchTimeout) {
clearTimeout(this.searchTimeout)
}
//
if (!this.keyword.trim()) {
this.showSearchResults = false
this.searchResults = []
return
}
// 300ms
this.searchTimeout = setTimeout(() => {
this.performRandomSearch()
}, 300)
},
//
performRandomSearch() {
if (!this.keyword.trim()) {
this.showSearchResults = false
this.searchResults = []
return
}
console.log('执行联想搜索:', { keyword: this.keyword, type: this.publish_type });
//
this.performSearch(this.keyword, this.publish_type);
},
//
performSearch(keyword, type) {
console.log('执行联想搜索:', { keyword, type });
reqSearch({
url_link: window.location.href,
keyword: keyword,
publish_type: type,
display_page: 'overview', // 使 overview
current_page: 1,
page_size: 15
}).then(res => {
this.searchResults = res.data.result
console.log('联想搜索结果:', res)
this.showSearchResults = true
// URL
console.log('联想搜索完成,显示结果');
}).catch(error => {
console.error('联想搜索失败:', error);
this.$message.error('搜索失败,请重试');
});
},
//
performFormalSearch(keyword, type) {
console.log('=== 执行正式搜索开始 ===');
console.log('搜索参数:', { keyword, type });
console.log('当前路由:', this.$route.path);
console.log('事件总线实例:', eventBus);
reqSearch({
url_link: window.location.href,
keyword: keyword,
publish_type: type,
display_page: 'list', // 使 list
current_page: 1,
page_size: 8
}).then(res => {
console.log('正式搜索结果:', res);
//
const eventData = {
type: type,
keyword: keyword,
data: res.data
};
console.log('准备发送事件数据:', eventData);
// 线
try {
eventBus.$emit('search', eventData);
console.log('事件总线事件发送成功');
} catch (error) {
console.error('事件总线事件发送失败:', error);
}
console.log('正式搜索完成,已触发搜索事件');
}).catch(error => {
console.error('正式搜索失败:', error);
this.$message.error('搜索失败,请重试');
});
},
// URL
updateURLParams(keyword, type) {
const query = { ...this.$route.query };
if (keyword) {
query.keyword = keyword;
} else {
delete query.keyword;
}
if (type) {
query.publish_type = type;
} else {
delete query.publish_type;
}
console.log('更新URL参数:', {
oldQuery: this.$route.query,
newQuery: query
});
// URL
this.$router.replace({
path: this.$route.path,
query: query
}).catch(err => {
//
if (err.name !== 'NavigationDuplicated') {
console.error('更新URL参数失败:', err);
} else {
console.log('URL参数更新重复忽略错误');
}
});
},
//
extractPlainText(htmlText) {
// DOM
const tempDiv = document.createElement('div');
tempDiv.innerHTML = htmlText;
return tempDiv.textContent || tempDiv.innerText || '';
},
//
handleTypeChange(value) {
console.log('选择的类型:', value === '1' ? '商品' : '需求', value)
this.publish_type = value
//
if (this.keyword.trim() && this.showSearchResults) {
this.performSearch(this.keyword, value);
}
},
//
handleSearch(result) {
//
if (event) {
event.stopPropagation();
event.preventDefault();
}
console.log('=== handleSearch 开始执行 ===');
console.log('接收到的参数:', result);
console.log('当前路由路径:', this.$route.path);
console.log('当前查询参数:', this.$route.query);
// result使product_namekeyword
if (result && result.product_name) {
console.log('使用联想结果作为搜索关键词');
const plainText = this.extractPlainText(result.product_name);
this.keyword = plainText;
console.log('提取的纯文本:', plainText);
//
this.showSearchResults = false;
this.searchResults = [];
} else {
console.log('使用输入框的关键词进行搜索');
// result
if (!this.keyword.trim()) {
this.$message.warning('请输入搜索关键词');
return;
}
}
//
console.log('准备执行搜索,关键词:', this.keyword, '类型:', this.publish_type);
//
const currentRoute = this.$route;
const currentDisplayPage = currentRoute.query.display_page;
if (currentDisplayPage !== 'list') {
//
console.log('跳转到搜索结果页面');
this.goSearch()
} else {
//
console.log('已在搜索结果页面,重新调用接口获取最新数据');
// URL
this.updateURLParams(this.keyword, this.publish_type);
//
this.performFormalSearch(this.keyword, this.publish_type);
}
console.log('=== handleSearch 执行完成 ===');
},
//
handleHotKeywordClick(keyword) {
this.keyword = keyword
this.handleSearch()
},
//
handleInputFocus() {
//
if (this.keyword.trim() && this.searchResults.length > 0) {
this.showSearchResults = true;
}
},
//
handleInputBlur() {
//
setTimeout(() => {
if (!this.$el.contains(document.activeElement)) {
this.hideSearchResults()
}
}, 100); // 100ms
},
//
hideSearchResults() {
this.showSearchResults = false
this.searchResults = []
},
//
handleGlobalClick(event) {
//
setTimeout(() => {
//
if (this.$el && this.$el.contains(event.target)) {
console.log('点击在搜索组件内部,不隐藏联想结果');
return;
}
//
if (this.showSearchResults) {
console.log('点击在搜索组件外部,隐藏联想结果');
this.hideSearchResults();
}
}, 100); // 100ms
},
//
highlightKeyword(item) {
console.log(item)
if (!this.keyword || !this.keyword.trim()) {
return item.product_name || item;
}
const keyword = this.keyword.trim();
// 使 product_name
const text = item.product_name || item;
console.log('高亮处理:', { keyword, text, item, textType: typeof text });
//
if (!keyword) {
return text;
}
//
if (!text || typeof text !== 'string') {
console.warn('文本不是字符串类型:', text);
return text;
}
//
const keywords = keyword.split(/\s+/).filter(k => k.length > 0);
let highlightedText = text;
//
keywords.forEach(key => {
if (key.length > 0) {
const regex = new RegExp(`(${this.escapeRegExp(key)})`, 'gi');
highlightedText = highlightedText.replace(regex, '<span class="highlighted-keyword">$1</span>');
}
});
console.log('高亮结果:', highlightedText);
return highlightedText;
},
//
escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
},
//
handleRouteChange(to, from) {
console.log('处理路由变化:', { to, from, currentKeyword: this.keyword });
const keyword = to.query.keyword;
const publishType = to.query.publish_type;
if (keyword) {
//
console.log('更新搜索状态:', { keyword, publishType });
this.keyword = keyword;
this.publish_type = publishType || '1'; //
//
this.showSearchResults = false;
this.searchResults = [];
//
this.$nextTick(() => {
this.reinitializeEventListeners();
});
//
//
console.log('搜索状态已更新,等待用户操作');
} else {
//
if (this.keyword || this.publish_type !== '1') {
console.log('清空搜索状态');
this.keyword = '';
this.publish_type = '1';
this.hideSearchResults();
}
}
},
//
reinitializeEventListeners() {
console.log('重新初始化事件监听器');
//
document.removeEventListener('click', this.handleGlobalClick);
//
document.addEventListener('click', this.handleGlobalClick);
//
this.showSearchResults = false;
this.searchResults = [];
console.log('事件监听器重新初始化完成');
}
},
//
mounted() {
console.log('=== 组件挂载开始 ===');
console.log('当前路由:', this.$route.path);
console.log('当前查询参数:', this.$route.query);
document.addEventListener('click', this.handleGlobalClick)
console.log('全局点击事件监听器已添加');
// URL
this.handleRouteChange(this.$route, null);
//
this.$watch('$route', (to, from) => {
console.log('路由变化监听:', { to, from });
this.handleRouteChange(to, from);
}, { immediate: true, deep: true });
console.log('=== 组件挂载完成 ===');
},
//
beforeDestroy() {
if (this.searchTimeout) {
clearTimeout(this.searchTimeout)
}
document.removeEventListener('click', this.handleGlobalClick)
}
}
</script>
<style scoped lang="scss">
.search-section {
border: 1px solid red;
flex: 1;
min-width: 850px;
padding-bottom: 10px;
.search-bar {
display: flex;
align-items: center;
@ -102,6 +569,14 @@ export default {
border: 2px solid #3f68d8;
border-radius: 4px;
overflow: hidden;
position: relative;
transition: border-radius 0.2s ease;
//
&.has-results {
border-bottom-color: transparent;
border-radius: 4px 4px 0 0;
}
.search-input {
flex: 1;
@ -109,12 +584,7 @@ export default {
border: none;
outline: none;
font-size: 14px;
}
.camera-icon {
padding: 0 10px;
color: #666;
cursor: pointer;
padding-left: 0;
}
.search-btn {
@ -131,12 +601,70 @@ export default {
}
}
//
.search-results {
position: absolute;
top: 42px;
left: 0;
right: 0;
background: white;
border: 2px solid #3f68d8;
border-top: none;
border-radius: 0 0 4px 4px;
z-index: 99999;
max-height: 300px;
overflow-y: auto;
.search-result-item {
padding: 12px 15px;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
&:hover {
background-color: #f5f7fa;
* {
color: #0864dd!important;
}
}
&:last-child {
border-bottom: none;
}
.result-title {
font-size: 14px;
color: #333;
}
.result-type {
font-size: 12px;
color: #909399;
padding: 2px 8px;
background: #f0f2f5;
border-radius: 10px;
}
}
}
.hot-search {
margin-top: 8px;
display: flex;
gap: 15px;
flex-wrap: wrap;
.search-label {
font-size: 14px;
color: #666;
margin-right: 10px;
}
.hot-keyword {
&:nth-child(-n+3) {
color: #e1251b;
@ -153,6 +681,26 @@ export default {
}
}
::v-deep .mySelect {
input {
border: none;
}
}
::v-deep .highlighted-keyword {
font-weight: bold;
color: #0864dd;
}
.logo-clickable {
cursor: pointer;
transition: opacity 0.2s ease;
&:hover {
opacity: 0.8;
}
}
@media (max-width: 768px) {
.search-section {
width: 100%;

View File

@ -0,0 +1,140 @@
<template>
<div class="searchBox">
<div v-if="loadData">
<!-- <div class="loading">
加载数据中...
</div> -->
</div>
<div v-else>
<div v-loading="loadData">
<productCard :productList="list"></productCard>
<el-pagination @current-change="handleCurrentChange" :current-page="current_page" :page-size="page_size"
layout="total, prev, pager, next" :total="total">
</el-pagination>
</div>
</div>
</div>
</template>
<script>
import { reqSearch } from '@/api/ncmatch'
import productCard from '../mainPage/productCard/index.vue'
import eventBus from '@/utils/eventBus'
export default {
name: 'searchBox',
components: {
productCard
},
data(){
return {
keyword: '',
publish_type: '',
display_page: '',
current_page: 1,
page_size: 8,
list:[],
total:0,
loadData:false
}
},
created(){
console.log('=== searchBox 组件创建 ===');
this.keyword = this.$route.query.keyword
this.publish_type = this.$route.query.publish_type
this.initData()
//
console.log('开始监听搜索事件');
eventBus.$on('search', this.handleSearch)
console.log('搜索事件监听器已添加');
},
mounted() {
console.log('=== searchBox 组件挂载 ===');
console.log('当前路由:', this.$route.path);
console.log('当前查询参数:', this.$route.query);
},
beforeDestroy() {
console.log('=== searchBox 组件销毁 ===');
//
eventBus.$off('search', this.handleSearch)
console.log('搜索事件监听器已移除');
},
methods: {
handleCurrentChange(val) {
console.log('分页变化:', val);
this.current_page = val;
this.initData();
},
handleSearch(searchData) {
console.log('=== searchBox 接收到搜索事件 ===');
console.log('搜索数据:', searchData);
console.log('当前组件状态:', {
keyword: this.keyword,
publish_type: this.publish_type,
list_length: this.list.length,
total: this.total
});
//
this.current_page = 1;
console.log('分页已重置到第1页');
//
if (searchData.data && searchData.data.result) {
console.log('更新搜索结果数据');
this.list = searchData.data.result;
this.total = searchData.data.total_count;
console.log('数据更新完成:', {
new_list_length: this.list.length,
new_total: this.total
});
} else {
console.warn('搜索数据格式不正确:', searchData);
}
//
this.keyword = searchData.keyword;
this.publish_type = searchData.type;
console.log('本地状态已更新:', {
new_keyword: this.keyword,
new_publish_type: this.publish_type
});
console.log('=== 搜索事件处理完成 ===');
},
initData(){
this.loadData = true
reqSearch({
url_link: window.location.href,
keyword: this.keyword,
publish_type: this.publish_type,
display_page: 'list',
current_page: this.current_page,
page_size: this.page_size
}).then(res => {
console.log("@@@@",res);
this.loadData = false
if(res.status){
this.list = res.data.result
this.total = res.data.total_count
}
})
}
}
}
</script>
<style scoped lang="scss">
.searchBox {
width: 100%;
}
</style>
<style lang="scss">
.searchBox {
width: 100%;
}
</style>

View File

@ -57,15 +57,20 @@
<!-- 产品 -->
<div class="product-card-container">
<productCard v-if="productList.length > 0" type="supplyAndDemandSquare" :publish_type="publish_type"
:productList="productList">
</productCard>
<div v-if="productList.length > 0">
<productCard type="supplyAndDemandSquare" :publish_type="publish_type"
:productList="productList">
</productCard>
<el-pagination @current-change="handleCurrentChange" :page-size="page_size"
layout="total, prev, pager, next" :total="total">
</el-pagination>
</div>
<div v-else class="no-data">
<img style="width: 150px;height: 10px;" src="./img/empty.svg" alt="">
暂无数据
</div>
</div>
</div>
</div>
</div>
@ -121,6 +126,10 @@ export default {
},
methods: {
handleCurrentChange(val) {
this.current_page = val
this.initData()
},
initAllData() {
// selectedCategory
this.init_product_category().then(() => {
@ -194,7 +203,7 @@ export default {
} else {
this.productList = res.data[0].product_list
}
this.total = res.data[0].total_count
}
})

View File

@ -0,0 +1,326 @@
<template>
<div>
<!-- 调试信息 -->
<div style="margin-bottom: 20px; padding: 15px; background: #f5f7fa; border-radius: 8px;">
<el-button size="small" @click="showDebugInfo">查看调试信息</el-button>
<el-button size="small" @click="showRawData">查看原始数据</el-button>
<el-button size="small" type="primary" @click="testTreeStructure">测试树形结构</el-button>
<el-button size="small" type="success" @click="getCategories">重新加载数据</el-button>
<span style="margin-left: 20px; color: #666;">
数据总数: {{ tableData.length }} |
树形层级: {{ getMaxLevel() }}
</span>
</div>
<el-table :data="tableData" style="width: 100%;margin-bottom: 20px;" row-key="id" border v-loading="loading"
element-loading-text="加载中..." element-loading-spinner="el-icon-loading"
:tree-props="{ children: 'children' }" :default-expand-all="false" :expand-on-click-node="false">
<el-table-column prop="name" label="名称" min-width="120">
</el-table-column>
<el-table-column prop="id" label="id" min-width="200">
</el-table-column>
<el-table-column prop="level" label="层级" min-width="120">
</el-table-column>
<el-table-column label="操作" width="280" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="small" @click="addNode(scope.row, 'sibling')">新增同级</el-button>
<el-button type="text" size="small" @click="addNode(scope.row, 'child')">新增子级</el-button>
<el-button type="text" size="small" @click="editNode(scope.row)">编辑</el-button>
<el-button type="text" size="small" @click="deleteNode(scope.row)"
style="color: #F56C6C;">删除</el-button>
</template>
</el-table-column>
</el-table>
<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-item>
</el-form>
</el-drawer>
</div>
</template>
<script>
import { reqNcMatchMenu } from '@/api/ncmatch';
export default {
name: 'menuMangement',
data() {
return {
loading: false,
categories: [],
tableData: [], //
drawer: false,
direction: 'rtl',
labelPosition: 'right',
formLabelAlign: {
name: '',
region: '',
type: ''
}
}
},
created() {
this.getCategories();
},
methods: {
addNode(row, type) {
this.drawer = true;
console.log('新增节点:', row, '类型:', type);
},
getCategories() {
this.loading = true;
reqNcMatchMenu({ url_link: window.location.href }).then(res => {
this.loading = false;
if (res.status) {
this.categories = this.buildSimpleTree(res.data);
this.tableData = this.categories; // 使
console.log("构建的精简树结构:", this.categories);
}
}).catch(error => {
this.loading = false;
console.error('获取菜单数据失败:', error);
});
},
editNode(row) {
this.drawer = true;
console.log('编辑节点:', row);
},
deleteNode(row) {
this.drawer = true;
console.log('删除节点:', row);
},
buildTree(data, parentId = null) {
if (!Array.isArray(data)) {
console.log('数据不是数组:', data);
return [];
}
console.log('构建树结构当前parentId:', parentId, '数据长度:', data.length);
//
const currentNodes = data.filter(item => {
// parentid null
if (parentId === null) {
return !item.parentid || item.parentid === '' || item.parentid === '0';
}
return item.parentid === parentId;
});
console.log('当前层级节点数量:', currentNodes.length, '节点:', currentNodes);
if (currentNodes.length === 0) return [];
//
return currentNodes.map(node => {
const resultNode = {
id: node.id,
name: node.name || '未命名',
parentid: node.parentid
};
//
const children = this.buildSimpleTree(data, node.id);
// children
if (children && children.length > 0) {
resultNode.children = children;
}
return resultNode;
});
},
//
buildSimpleTree(data, parentId = null) {
if (!Array.isArray(data)) {
return [];
}
//
const currentNodes = data.filter(item => {
if (parentId === null) {
return !item.parentid || item.parentid === '' || item.parentid === '0';
}
return item.parentid === parentId;
});
if (currentNodes.length === 0) return [];
//
return currentNodes.map(node => {
const resultNode = {
id: node.id,
name: node.name || '未命名',
parentid: node.parentid
};
//
const children = this.buildSimpleTree(data, node.id);
// children
if (children && children.length > 0) {
resultNode.children = children;
}
return resultNode;
});
},
//
calculateLevel(data, nodeId) {
let level = 1;
let currentId = nodeId;
while (true) {
const parent = data.find(item => item.id === currentId);
if (!parent || !parent.parentid || parent.parentid === '' || parent.parentid === '0') {
break;
}
level++;
currentId = parent.parentid;
}
return level;
},
showDebugInfo() {
console.log("当前 tableData 的结构:", this.tableData);
console.log("当前 tableData 的层级分布:", this.getLevelDistribution());
console.log("当前 tableData 的最大层级:", this.getMaxLevel());
//
this.checkTreeStructure(this.tableData);
},
//
checkTreeStructure(nodes, level = 0) {
if (!Array.isArray(nodes)) return;
nodes.forEach(node => {
const indent = ' '.repeat(level);
console.log(`${indent}📁 ${node.name} (ID: ${node.id})`);
console.log(`${indent} - hasChildren: ${node.hasChildren}`);
console.log(`${indent} - children.length: ${node.children ? node.children.length : 'undefined'}`);
console.log(`${indent} - level: ${node.level}`);
if (node.children && node.children.length > 0) {
this.checkTreeStructure(node.children, level + 1);
}
});
},
showRawData() {
console.log("原始数据:", this.categories);
},
//
testTreeStructure() {
const testData = [
{
id: 1,
name: '一级菜单1',
parentid: null
},
{
id: 2,
name: '一级菜单2',
parentid: null
},
{
id: 3,
name: '一级菜单3',
parentid: null,
children: [
{
id: 31,
name: '二级菜单3-1',
parentid: 3
},
{
id: 32,
name: '二级菜单3-2',
parentid: 3,
children: [
{
id: 321,
name: '三级菜单3-2-1',
parentid: 32
}
]
}
]
}
];
console.log("精简测试数据:", testData);
this.tableData = testData;
},
getLevelDistribution() {
const levels = {};
this.tableData.forEach(node => {
const level = node.level;
if (levels[level]) {
levels[level]++;
} else {
levels[level] = 1;
}
});
return levels;
},
getMaxLevel() {
let maxLevel = 0;
this.tableData.forEach(node => {
if (node.level > maxLevel) {
maxLevel = node.level;
}
});
return maxLevel;
}
}
}
</script>
<style scoped>
.el-table {
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.el-table th {
background-color: #f5f7fa;
color: #606266;
font-weight: 600;
}
.el-table td {
padding: 12px 0;
}
.el-button--text {
padding: 4px 8px;
margin: 0 2px;
}
.el-button--text:hover {
background-color: #f5f7fa;
border-radius: 4px;
}
.el-tag {
border-radius: 4px;
}
/* 树形表格的缩进样式 */
.el-table__expand-icon {
margin-right: 8px;
}
/* 加载状态样式 */
.el-loading-mask {
background-color: rgba(255, 255, 255, 0.8);
}
</style>

View File

@ -24,5 +24,5 @@ export function getHomePath() {
}
}
console.log("res是",homePath)
return "/homePage/index"
return homePath
}