main #52

Merged
charles merged 16 commits from main into prod 2025-12-08 17:20:04 +08:00
43 changed files with 4789 additions and 219 deletions

View File

@ -802,7 +802,15 @@ async def baidu_confirm_auto_renew_order(ns={}):
renew_status = await get_baidu_orderlist({'order_id': order_id, 'userid': user_id})
if renew_status.get('status'):
renew_status_count += 1
else:
ns_err_log = {
'id': uuid(),
'log_level': 'ERROR',
'log_content': '续费订单 %s 失败 %s' % (order_id, str(renew_status)),
'user_id': user_id,
'request_url': '/baiduc/baidu_confirm_auto_renew_order.dspy'
}
await sor.C('warn_error_log', ns_err_log)
# 并触发update函数
update_msg = await update_baidu_order_list({'orgid': orgid})

View File

@ -745,11 +745,32 @@ async def update_baidu_order_list(ns={}):
}
}
async def baidu_orderdetail_by_uuid(ns={}):
order_id = ns.get('order_id')
baidu_id = ns.get('baidu_id')
method = 'POST'
nss = {'uuids': [order_id], 'queryAccountId': 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()
return {
'status': True,
'msg': 'get baidu order detail success',
'data': data_
}
async def baidu_confirm_refund_order(ns={}):
# ns = {
# 'order_id': ["2996f0baf34c4a0a98e1da0b4e290a35"],
# 'userid': 'y_xQK0G62dtZT5EneMQFT'
# }
import asyncio
# 把 NEED_CONFIRM的订单同步到本地库用于后续状态更新
await update_baidu_order_list({'userid': ns.get('userid')})
@ -764,18 +785,21 @@ async def baidu_confirm_refund_order(ns={}):
'msg': '用户 %s 未绑定百度智能云账号' % ns.get('userid')
}
orders = json.loads(ns.get('order_id')) if isinstance(ns.get('order_id'), str) else ns.get('order_id')
refund_orders_base_list = json.loads(ns.get('order_id')) if isinstance(ns.get('order_id'), str) else ns.get('order_id')
for order_id in orders:
for order_id in refund_orders_base_list:
with open('baidu_refund_order_id.txt', 'a+') as f:
f.write('%s 当前退订订单ID: %s ' % (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), order_id))
db = DBPools()
async with db.sqlorContext('kboss') as sor:
refund_status_li = await sor.R('baidu_orders', {'orderid': order_id})
refundstatus = refund_status_li[0]['refundstatus']
refund_id = refund_status_li[0]['id']
ns_record_li = await sor.R('user_action', {'orderid': order_id})
ns_record_id = ns_record_li[0]['id'] if ns_record_li else None
ns_record = {'id': ns_record_id} if ns_record_id else None
# ns_record_li = await sor.R('user_action', {'orderid': order_id})
# ns_record_id = ns_record_li[0]['id'] if ns_record_li else None
# ns_record = {'id': ns_record_id} if ns_record_id else None
if not refundstatus:
# data_ = {}
@ -787,6 +811,7 @@ async def baidu_confirm_refund_order(ns={}):
header = {
"Host": "billing.baidubce.com"
}
await asyncio.sleep(0.5)
header = await get_auth_header(method=method, url=url, header=header)
async with aiohttp_client.request(
method=method,
@ -807,64 +832,94 @@ async def baidu_confirm_refund_order(ns={}):
db = DBPools()
async with db.sqlorContext('kboss') as sor:
await sor.U('baidu_orders', {'id': refund_id, 'refundstatus': '1'})
# 增加延迟
await asyncio.sleep(4)
# 延迟2-3秒还是获取到 ready状态的订单那就重复请求一次目的是尽快刷新状态
async with aiohttp_client.request(
method=method,
url=url,
headers=header,
json=paydata) as res:
await res.json()
# 把 NEED_CONFIRM的本地库改为CREATED
await update_baidu_order_list({'userid': ns.get('userid')})
with open('baidu_refund_order_id.txt', 'a+') as f:
f.write('远程退订成功: %s \n' % str(data_))
else:
ns_record = {
'orderid': order_id,
'ordertype': 'REFUND',
'userid': ns.get('userid'),
'reason': '产品退费失败, %s' % str(data_)[:400]
}
await user_action_record(ns_record)
return {
'status': False,
'msg': '产品退款出错!%s' % str(data_)[:400]
}
}
with open('baidu_refund_order_id.txt', 'a+') as f:
f.write('远程退订失败: %s ' % str(data_))
db = DBPools()
async with db.sqlorContext('kboss') as sor:
await asyncio.sleep(3)
order_status_dict = await baidu_orderdetail_by_uuid({'order_id': order_id, 'baidu_id': ns.get('baidu_id')})
if order_status_dict.get('data') and order_status_dict['data'].get('orders') and order_status_dict['data']['orders'][0]['status'] == 'NEED_CONFIRM':
await user_action_record(ns_record)
# 从列表中移除该订单ID
refund_orders_base_list.remove(order_id)
ns_err_log = {
'id': uuid(),
'log_level': 'ERROR',
'log_content': '检测到 产品仍在使用中 暂时不退费, 订单详情 %s' % str(order_status_dict['data']),
'user_id': ns.get('userid'),
'request_url': '/baiduc//baidu_confirm_refund_order.dspy',
'request_params': json.dumps({'order_id': order_id, 'baidu_id': ns.get('baidu_id')}),
}
await sor.C('warn_error_log', ns_err_log)
with open('baidu_refund_order_id.txt', 'a+') as f:
f.write('检测到 产品仍在使用中 暂时不退费 %s \n' % str(order_status_dict['data']))
else:
ns_record['reason'] = '远程退款失败, 产品已释放, 订单状态 %s, 正常退费' % order_status_dict['data']['orders'][0]['status']
await user_action_record(ns_record)
with open('baidu_refund_order_id.txt', 'a+') as f:
f.write('检测到 产品已释放,订单状态 %s, 继续进入退费程序 \n' % order_status_dict['data']['orders'][0]['status'])
if refundstatus == '2':
"""
退款状态为2, 说明退款成功, 无需继续处理
"""
refund_orders_base_list.remove(order_id)
continue
# 分割
# 增加延迟
await asyncio.sleep(5)
# 把 NEED_CONFIRM的本地库改为CREATED
await update_baidu_order_list({'userid': ns.get('userid')})
for order_id in refund_orders_base_list:
# 获取created状态后再去退款
local_refund_status = await get_baidu_orderlist({'order_id': order_id, 'userid': ns.get('userid')})
print('local_refund_status', local_refund_status)
if local_refund_status.get('status'):
db = DBPools()
async with db.sqlorContext('kboss') as sor:
refund_status_li = await sor.R('baidu_orders', {'orderid': order_id})
refundstatus = refund_status_li[0]['refundstatus']
refund_id = refund_status_li[0]['id']
baidu_orders_status_update = """update baidu_orders set refundstatus='2' where id='%s';""" % refund_id
await sor.sqlExe(baidu_orders_status_update, {})
await sor.U('user_action', {'id': ns_record.get('id'), 'ordertype': 'REFUND', 'reason': '远程退款成功, 本地客户退款成功'})
ns_record_li = await sor.R('user_action', {'orderid': order_id})
ns_record_id = ns_record_li[0]['id'] if ns_record_li else None
await sor.U('user_action', {'id': ns_record_id, 'ordertype': 'REFUND', 'reason': '远程退款成功, 本地客户退款成功'})
continue
# return {
# 'status': True,
# 'msg': '百度云给平台退款成功,平台给客户退款成功'
# }
else:
if local_refund_status.get('msg') == 'delay_order':
return {
'status': False,
'msg': '百度远程订单还未生成, 请十秒后重试, %s' % str(local_refund_status)
}
return {
'status': False,
'msg': '百度云退款成功,本机构给客户退款出错!, %s' % str(local_refund_status)
ns_err_log = {
'id': uuid(),
'log_level': 'ERROR',
'log_content': '远程退款成功,本地退款失败 %s' % str(local_refund_status),
'user_id': ns.get('userid'),
'request_url': '/baiduc//baidu_confirm_refund_order.dspy',
'request_params': json.dumps({'order_id': order_id, 'baidu_id': ns.get('baidu_id')}),
}
await sor.C('warn_error_log', ns_err_log)
ns_record = {
'orderid': order_id,
'ordertype': 'REFUND',
'userid': ns.get('userid'),
'reason': '远程退款成功,本地退款失败, %s' % str(local_refund_status)
}
await user_action_record(ns_record)
continue
return {
'status': True,

View File

@ -0,0 +1,97 @@
AK = 'ALTAKPk92fX9cgGDax83yNL8mP'
SK = '9b16b8efd4dc463d8bbd5462db1db8a5'
async def get_auth_header(method: str, url: str, header: dict, query={}, get_res=False):
import urllib
import hmac
# 解析uri
url_parse = urllib.parse.urlparse(url)
uri = url_parse.path
url_query = url_parse.query
# 获取query
if url_query:
query = dict(urllib.parse.parse_qsl(url_query))
# 2.x-bce-date
x_bce_date = time.gmtime()
x_bce_date = time.strftime('%Y-%m-%dT%H:%M:%SZ', x_bce_date)
# 4.认证字符串前缀
authStringPrefix = "bce-auth-v1" + "/" + AK + "/" + x_bce_date + "/" + "1800"
# windows下为urllib.parse.quoteLinux下为urllib.quote
# 5.生成CanonicalRequest
# 5.1生成CanonicalURI
CanonicalURI = urllib.parse.quote(uri)
# 如果您调用的接口的query比较复杂的话需要做额外处理
# 5.2生成CanonicalQueryString
# CanonicalQueryString = query
result = ['%s=%s' % (k, urllib.parse.quote(str(v))) for k, v in query.items() if k.lower != 'authorization']
result.sort()
CanonicalQueryString = '&'.join(result)
# 5.3生成CanonicalHeaders
result = []
signedHeaders_list = []
for key,value in header.items():
tempStr = str(urllib.parse.quote(key.lower(),safe="")) + ":" + str(urllib.parse.quote(value,safe=""))
signedHeaders_list.append(str(urllib.parse.quote(key.lower(),safe="")))
result.append(tempStr)
result.sort()
signedHeaders = ';'.join(sorted(signedHeaders_list))
CanonicalHeaders = "\n".join(result)
# 5.4拼接得到CanonicalRequest
CanonicalRequest = method + "\n" + CanonicalURI + "\n" + CanonicalQueryString +"\n" + CanonicalHeaders
# 6.生成signingKey
signingKey = hmac.new(SK.encode('utf-8'), authStringPrefix.encode('utf-8'), hashlib.sha256)
# 7.生成Signature
Signature = hmac.new((signingKey.hexdigest()).encode('utf-8'), CanonicalRequest.encode('utf-8'), hashlib.sha256)
# 8.生成Authorization并放到header里
header['Authorization'] = authStringPrefix + "/" + signedHeaders + "/" + Signature.hexdigest()
if get_res:
# 异步请求链接 返回结果
async with aiohttp_client.request(
method=method,
url=url,
headers=header,
allow_redirects=True,
json=query) as res:
return res
else:
return header
async def get_orderdetail_by_uuid(ns={}):
order_id = ns.get('order_id')
baidu_id = ns.get('baidu_id')
method = 'POST'
nss = {'uuids': [order_id], 'queryAccountId': 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()
return {
'status': True,
'msg': 'get baidu order detail success',
'data': data_
}
ret = await get_orderdetail_by_uuid(params_kw)
return ret

View File

@ -84,6 +84,10 @@ async def affirmbz_order(ns={}):
# 处理购买逻辑
else:
if j.get('chargemode') == 'postpay' and j.get('orderkey') == 'snapshot':
# 快照后付费不创建客户产品记录
continue
product = await sor.R('product', {'id': j['productid']})
nss = {}
nss['id'] = uuid()

View File

@ -582,7 +582,7 @@ async def get_firstpage_product_tree(ns={}):
{
'id': '211',
'thrTitle': None,
'value': [{'id': '2111', 'name': '容器云'},
'value': [#{'id': '2111', 'name': '容器云'},
{'id': '2113', 'name': '裸金属'},
#{'id': '2114', 'name': '裸金属-910B'},
{'id': '2115', 'name': '一体机-昆仑芯'},

View File

@ -21,6 +21,8 @@ async def baidu_query_by_expire_time(ns={}):
'msg': 'User not synchronized'
}
ns['queryAccountId'] = baiduid
ns['pageNo'] = int(ns.get('pageno', 1))
ns['pageSize'] = int(ns.get('pagesize', 100))
method = 'POST'
ns_format = '&'.join(['%s=%s' % (k, v) for k, v in ns.items()])
url = 'https://billing.baidubce.com/v1/resource/queryByExpireTime?%s' % ns_format
@ -35,11 +37,18 @@ async def baidu_query_by_expire_time(ns={}):
json=ns) as res:
data_ = await res.json()
print('data_', data_)
return {
'status': True,
'msg': 'get baidu resource expire time success',
'data': data_
}
if data_.get('pageSize'):
return {
'status': True,
'msg': 'get baidu resource expire time success',
'data': data_
}
else:
return {
'status': False,
'msg': 'get baidu resource expire time failed',
'data': data_
}
async def calculate_time_diff(time_str=None):
# 将字符串时间转换为 datetime 对象
@ -63,22 +72,46 @@ async def get_resource_expire_time(ns={}):
else:
userid = await get_user()
data = []
baidu_resource_data = await baidu_query_by_expire_time({'userid': userid})
baidu_resource_data = await baidu_query_by_expire_time(ns)
if baidu_resource_data.get('data'):
for baidubaidu_resource in baidu_resource_data['data']['result']:
baidu_r = {
'id': uuid(),
'name': baidubaidu_resource['serviceTypeName'],
'instanceid': baidubaidu_resource['shortId'],
'status': baidubaidu_resource['status'],
'expiretime': await time_convert(baidubaidu_resource['expireTime']),
'days': await calculate_time_diff(baidubaidu_resource['expireTime'])
}
data.append(baidu_r)
data_result = baidu_resource_data['data']['result'] if baidu_resource_data['data'].get('result') else []
# if not data_result:
# return {
# 'status': True,
# 'msg': '无资源数据',
# 'data': str(baidu_resource_data['data'])
# }
for baidubaidu_resource in data_result:
baidubaidu_resource['id'] = uuid()
baidubaidu_resource['name'] = baidubaidu_resource['serviceTypeName']
baidubaidu_resource['instanceid'] = baidubaidu_resource['shortId']
baidubaidu_resource['expiretime'] = await time_convert(baidubaidu_resource['expireTime'])
baidubaidu_resource['days'] = await calculate_time_diff(baidubaidu_resource['expireTime'])
# 读取数据库表product,匹配跳转链接
db = DBPools()
async with db.sqlorContext('kboss') as sor:
find_sql = """select * from product where providerpid='baidu_%s' and del_flg='0';""" % baidubaidu_resource['serviceType']
product_li = await sor.sqlExe(find_sql, {})
spec_note_li = json.loads(product_li[0]['spec_note']) if product_li else []
for spec_note in spec_note_li:
if spec_note.get('configName') == 'listUrl':
baidubaidu_resource['list_url'] = spec_note.get('value')
break
else:
baidubaidu_resource['list_url'] = ''
data.append(baidubaidu_resource)
return {
'status': True,
'msg': 'get resouce expire time success',
'data': data
'data': data,
# 分页
'pagination': {
'total': baidu_resource_data['data'].get('totalCount'),
'page_size': baidu_resource_data['data'].get('pageSize'),
'current_page': baidu_resource_data['data'].get('pageNo')
}
}
ret = await get_resource_expire_time(params_kw)

View File

@ -0,0 +1,50 @@
async def home_page_content_add(ns={}):
"""
添加首页内容项
:param ns: 包含content_type, title, description等字段的字典
:return: 创建结果
"""
ns_dic = {
'id': uuid(), # 固定写法
'menu_product_id': ns.get('menu_product_id'),
'parent_id': ns.get('parent_id'),
'level': ns.get('level', 1),
'content_type': ns.get('content_type'), # advantage, feature, application, product
'sort_order': ns.get('sort_order', 0),
'title': ns.get('title'),
'description': ns.get('description'),
'img': ns.get('img'),
'name': ns.get('name'),
'price': ns.get('price'),
'pre_price': ns.get('pre_price'),
'price_unit': ns.get('price_unit'),
'discount': ns.get('discount'),
'bg_img_url': ns.get('bg_img_url'),
'icon': ns.get('icon')
}
# 必填字段验证
if not ns_dic.get('content_type'):
return {
'status': False,
'msg': 'content_type is required'
}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
await sor.C('home_page_content_items', ns_dic)
return {
'status': True,
'msg': 'create home page content success',
'data': {'id': ns_dic['id']}
}
except Exception as e:
await sor.rollback()
return {
'status': False,
'msg': 'create home page content failed, %s' % str(e)
}
ret = await home_page_content_add(params_kw)
return ret

View File

@ -0,0 +1,33 @@
async def home_page_content_delete(ns={}):
"""
软删除内容项id值必传并且把del_flg值修改为1
:param ns: 包含id的字典
:return: 删除结果
"""
if not ns.get('id'):
return {
'status': False,
'msg': 'id is required'
}
ns_dic = {
'id': ns.get('id'),
'del_flg': '1'
}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
await sor.U('home_page_content_items', ns_dic)
return {
'status': True,
'msg': 'delete home page content success'
}
except Exception as e:
await sor.rollback()
return {
'status': False,
'msg': 'delete home page content failed, %s' % str(e)
}
ret = await home_page_content_delete(params_kw)
return ret

View File

@ -0,0 +1,118 @@
async def home_page_content_get_tree(ns={}):
"""
获取首页内容的树形结构按content_type分组
:param ns: 包含menu_product_id的字典
:return: 树形结构数据
"""
menu_product_id = ns.get('menu_product_id')
if not menu_product_id:
return {
'status': False,
'msg': 'menu_product_id is required'
}
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
# 查询所有有效的内容项
sql = """
SELECT * FROM home_page_content_items
WHERE menu_product_id = '%s' AND del_flg = '0'
ORDER BY sort_order ASC, create_at ASC
""" % menu_product_id
all_items = await sor.sqlExe(sql, {})
# 通过home_page_product_info表查找标题和描述
find_sql = """
SELECT * FROM home_page_product_info
WHERE id = '%s' AND del_flg = '0'
""" % menu_product_id
product_info = await sor.sqlExe(find_sql, {})
product_title = None
product_description = None
if product_info:
product_info = product_info[0]
product_title = product_info.get('name', '')
product_description = product_info.get('description', '')
# 按content_type分组
result = {
'title': product_title, # 可以从其他表获取
'description': product_description, # 可以从其他表获取
'advantages': [],
'features': [],
'applications': [],
'products': []
}
for item in all_items:
content_type = item.get('content_type')
item_data = {
'id': item['id'],
'title': item['title'],
'description': item['description'],
'img': item['img'],
'icon': item['icon']
}
# 根据内容类型添加到对应的列表
if content_type == 'advantage':
result['advantages'].append(item_data)
elif content_type == 'feature':
result['features'].append(item_data)
elif content_type == 'application':
# 应用场景可能包含子项
app_data = item_data.copy()
if not item.get('parent_id'): # 顶级应用
app_data['provide'] = [] # 子项将在后续处理
result['applications'].append(app_data)
elif content_type == 'product' and not item.get('parent_id'):
# 产品可能包含配置项
product_data = item_data.copy()
product_data['price'] = item.get('price')
product_data['pre_price'] = item.get('pre_price')
product_data['price_unit'] = item.get('price_unit')
product_data['discount'] = item.get('discount')
product_data['bg_img_url'] = item.get('bg_img_url')
product_data['list'] = [] # 子项将在后续处理
result['products'].append(product_data)
# 处理子级项目
for item in all_items:
if item.get('parent_id') != '0':
parent_id = item.get('parent_id')
content_type = item.get('content_type')
# 查找父级并添加子项
if content_type == 'application':
for app in result['applications']:
if app['id'] == parent_id:
app['provide'].append({
'id': item['id'],
'title': item['title'],
'description': item['description']
})
elif content_type == 'product':
for product in result['products']:
if product['id'] == parent_id:
product['list'].append({
'id': item['id'],
'name': item['name'],
'description': item['description'],
'icon': item['icon']
})
return {
'status': True,
'msg': 'get home page content tree success',
'data': result
}
except Exception as e:
return {
'status': False,
'msg': 'get home page content tree failed, %s' % str(e)
}
ret = await home_page_content_get_tree(params_kw)
return ret

View File

@ -0,0 +1,36 @@
async def home_page_content_search(ns={}):
"""
查找内容项支持按内容类型、父级ID等条件查询
:param ns: 查询条件字典
:return: 查询结果
"""
# 构建查询条件
conditions = {"del_flg": "0"}
if ns.get('content_type'):
conditions['content_type'] = ns.get('content_type')
if ns.get('parent_id'):
conditions['parent_id'] = ns.get('parent_id')
if ns.get('menu_product_id'):
conditions['menu_product_id'] = ns.get('menu_product_id')
if ns.get('id'):
conditions['id'] = ns.get('id')
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
result = await sor.R('home_page_content_items', conditions)
return {
'status': True,
'msg': 'search home page content success',
'data': result
}
except Exception as e:
await sor.rollback()
return {
'status': False,
'msg': 'search home page content failed, %s' % str(e)
}
ret = await home_page_content_search(params_kw)
return ret

View File

@ -0,0 +1,39 @@
async def home_page_content_update(ns={}):
"""
更新内容项
:param ns: 包含id和需要更新的字段的字典
:return: 更新结果
"""
if not ns.get('id'):
return {
'status': False,
'msg': 'id is required'
}
# 构建更新字段,排除空值
ns_dic = {'id': ns.get('id')}
update_fields = ['title', 'description', 'img', 'name', 'price',
'pre_price', 'price_unit', 'discount', 'bg_img_url',
'icon', 'sort_order', 'parent_id', 'level']
for field in update_fields:
if ns.get(field):
ns_dic[field] = ns.get(field)
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
await sor.U('home_page_content_items', ns_dic)
return {
'status': True,
'msg': 'update home page content success'
}
except Exception as e:
await sor.rollback()
return {
'status': False,
'msg': 'update home page content failed, %s' % str(e)
}
ret = await home_page_content_update(params_kw)
return ret

View File

@ -13,13 +13,13 @@ async def home_page_product_menu_add(ns={}):
# 验证必填字段
if not ns_dic.get('menu_level') or not ns_dic.get('title'):
return {
'status': False,
'msg': 'menu_level and title are required'
}
# if not ns_dic.get('menu_level') or not ns_dic.get('title'):
# return {
# 'status': False,
# 'msg': 'menu_level and title are required'
# }
if ns.get('menu_level') > 1 and not ns.get('parent_id'):
if int(ns.get('menu_level')) > 1 and not ns.get('parent_id'):
return {
'status': False,
'msg': 'parent_id is required for menu_level > 1'

View File

@ -165,7 +165,7 @@ async def build_menu_tree(menu_list, target_level=None, target_title=None):
"""
# 通过sort_order对菜单进行排序
menu_list.sort(key=lambda x: x['sort_order'], reverse=True)
menu_list.sort(key=lambda x: int(x['sort_order']), reverse=False)
menu_dict = {}
result = []

View File

@ -14,12 +14,12 @@ async def home_page_product_menu_update(ns={}):
ns_dic = {'id': ns.get('id')}
if ns.get('title'):
ns_dic['title'] = ns.get('title')
if ns.get('parent_id') is not None:
if ns.get('parent_id'):
ns_dic['parent_id'] = ns.get('parent_id')
if ns.get('menu_level'):
ns_dic['menu_level'] = ns.get('menu_level')
if ns.get('sort_order') is not None:
ns_dic['sort_order'] = ns.get('sort_order')
if ns.get('sort_order'):
ns_dic['sort_order'] = int(ns.get('sort_order'))
db = DBPools()
async with db.sqlorContext('kboss') as sor:

View File

@ -14,21 +14,21 @@ async def home_page_product_update(ns={}):
ns_dic = {'id': ns.get('id')}
if ns.get('name'):
ns_dic['name'] = ns.get('name')
if ns.get('description') is not None:
if ns.get('description'):
ns_dic['description'] = ns.get('description')
if ns.get('label') is not None:
if ns.get('label'):
ns_dic['label'] = ns.get('label')
if ns.get('product_group') is not None:
if ns.get('product_group'):
ns_dic['product_group'] = ns.get('product_group')
if ns.get('url') is not None:
if ns.get('url'):
ns_dic['url'] = ns.get('url')
if ns.get('list_url') is not None:
if ns.get('list_url'):
ns_dic['list_url'] = ns.get('list_url')
if ns.get('icon_url') is not None:
if ns.get('icon_url'):
ns_dic['icon_url'] = ns.get('icon_url')
if ns.get('source') is not None:
if ns.get('source'):
ns_dic['source'] = ns.get('source')
if ns.get('sort_order') is not None:
if ns.get('sort_order'):
ns_dic['sort_order'] = ns.get('sort_order')
if ns.get('menu_id'):
ns_dic['menu_id'] = ns.get('menu_id')

Binary file not shown.

View File

@ -0,0 +1,9 @@
import request from "@/utils/request";
export const reqHotProduct = () => {
return request({
url: '/product/get_firstpage_jizuonet.dspy',
method: 'post',
})
}

View File

@ -0,0 +1,117 @@
import request from "@/utils/request";
// 菜单添加
export function addProductMenuAPI(params) {
return request({
url: `/product/home_page_product_menu_add.dspy`,
method: 'post',
data: params,
})
}
// 菜单更新
export function updateProductMenuAPI(params) {
return request({
url: `/product/home_page_product_menu_update.dspy`,
method: 'post',
data: params,
})
}
// 菜单查找
export function findProductMenuAPI(params) {
return request({
url: `/product/home_page_product_menu_search.dspy`,
method: 'get',
data: params,
})
}
// 菜单删除
export function deleteProductMenuAPI(params) {
return request({
url: `/product/home_page_product_menu_delete.dspy`,
method: 'post',
data: params,
})
}
// 产品添加
export function addProductThreeMenuAPI(params) {
return request({
url: `/product/home_page_product_add.dspy`,
method: 'post',
data: params,
})
}
// 产品列表
export function findProductThreeMenuAPI(params) {
return request({
url: `/product/home_page_product_search.dspy`,
method: 'post',
data: params,
})
}
// 三级产品更新
export function updateProductThreeMenuAPI(params) {
return request({
url: `/product/home_page_product_update.dspy`,
method: 'post',
data: params,
})
}
// 三级产品删除
export function deleteProductThreeMenuAPI(params) {
return request({
url: `/product/home_page_product_delete.dspy`,
method: 'post',
data: params,
})
}
//产品介绍页数据添加
export function addProductIntroAPI(params) {
return request({
url: `/product/home_page_content_add.dspy`,
method: 'post',
data: params,
})
}
// 产品介绍页数据删除
export function deleteProductIntroAPI(params) {
return request({
url: `/product/home_page_content_delete.dspy`,
method: 'get',
data: params,
})
}
// 产品介绍页数据更新
export function updateProductIntroAPI(params) {
return request({
url: `/product/home_page_content_update.dspy`,
method: 'post',
data: params,
})
}
// 产品介绍页数据树
export function findProductIntroAPI(params) {
return request({
url: `/product/home_page_content_get_tree.dspy`,
method: 'post',
data: params,
})
}
// 产品介绍页数据查找
export function findProductIntroTreeAPI(params) {
return request({
url: `/product/home_page_content_search.dspy`,
method: 'get',
data: params,
})
}

View File

@ -21,7 +21,7 @@ import CreateEip from "@/views/product/productHome/capitalOnline/Net/Eip/createE
import { getHomePath } from '@/views/setting/tools'
Vue.use(Router);
/**
* Note: sub-menu only appear when route children.length >= 1
* Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
@ -80,7 +80,16 @@ export const constantRoutes = [
title: '产品详情',
component: () => import('@/views/homePage/wxDetailPage.vue'),
hidden: true
}, {
},
{
path: '/h5HomePage',
name: 'H5HomePage',
title: 'H5首页',
component: () => import('@/views/H5/indexPage/index.vue'),
hidden: true,
},
{
hidden: true, path: '/screen', name: 'screen', title: '可视化大屏', meta: {
title: "可视化大屏", fullPath: "/operation/analyze/screen",
}, component: () => import('@/views/product/bigScreen/index.vue'), children: [{
@ -1156,8 +1165,17 @@ export const asyncRoutes = [
path: "index",
component: () => import("@/views/operation/menuMangement/index.vue"),
name: "menuMangement",
meta: { title: "菜单管理", fullPath: "/menuMangement/index" },
meta: { title: "ncmatch菜单管理", fullPath: "/menuMangement/index" },
},
{
path: "productsServices",
component: () => import("@/views/operation/productsServices/index.vue"),
name: "productsServices",
meta: {
title: "产品与服务管理",
fullPath: "/operation/productsServices"
}
}
]
},

View File

@ -145,6 +145,22 @@ const mutations = {
};
const actions = {
/**
* 生成动态路由
*
* 根据用户类型组织类型和权限列表生成对应的动态路由配置
* 包含管理员和普通用户的不同路由生成逻辑
*
* @param {Object} context - Vuex上下文对象
* @param {Function} context.commit - 提交mutation的方法
* @param {Object} context.rootState - 根模块的状态
* @param {Object} params - 参数对象
* @param {string} [params.userType] - 用户类型
* @param {number} [params.orgType] - 组织类型
* @param {Array} [params.auths] - 权限列表
* @param {Object} [params.user] - 用户信息对象
* @returns {Promise<Array>} 解析后的动态路由数组
*/
generateRoutes({ commit, rootState }, params) {
console.log("ACTION generateRoutes - params:", params);
return new Promise((resolve) => {

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -0,0 +1,392 @@
<template>
<div class="main-page">
<!-- banner图 -->
<div class="top-box">
<div class="title">
<p class="title-top">
<span class="name">开元云</span> 您身边的AI管家
</p>
<p class="title-btm">
全球领先的AI服务运营商
</p>
</div>
</div>
<!-- 云筑企业基座 -->
<div class="base-box">
<div class="text">
<p class="text-top">云筑企业基座</p>
<p class="text-btm">多云结合 让上云更简单</p>
</div>
<!-- 内容 -->
<div class="content">
<div class="item-box" v-for="(item, index) in baseData" :key="index">
<!-- 标题 -->
<div class="item-title">
{{ item.title }}
</div>
<!-- 介绍 -->
<div class="item-description">
{{ item.description }}
</div>
<!-- 内容 -->
<div class="item-content">
{{ item.content }}
</div>
<div class="icon-box">
</div>
<!-- 价格 -->
<div class="item-price">
<span class="price-icon"></span> <span class="price">{{ item.price }}</span> /
</div>
<!-- 按钮 -->
<div class="item-button">
<div class="item-button-text">立即咨询</div>
</div>
</div>
</div>
</div>
<!-- 智算未来征程 -->
<div class="journey-box">
<div class="text">
<p class="text-top">智算未来征程</p>
<p class="text-btm">多元异构 灵活短租</p>
</div>
<!-- 内容 -->
<div class="content">
<div class="item-box" v-for="(item, index) in baseData" :key="index">
<!-- 标题 -->
<div class="item-title">
{{ item.title }}
</div>
<!-- 介绍 -->
<div class="item-description">
{{ item.description }}
</div>
<!-- 内容 -->
<div class="item-content">
{{ item.content }}
</div>
<div class="icon-box">
</div>
<!-- 价格 -->
<div class="item-price">
<span class="price-icon"></span> <span class="price">{{ item.price }}</span> /
</div>
<!-- 按钮 -->
<div class="item-button">
<div class="item-button-text">立即咨询</div>
</div>
</div>
</div>
</div>
<!-- 网织智能经纬 -->
<div class="latitude-box">
<div class="text">
<p class="text-top">网织智能经纬</p>
<p class="text-btm">云算网结合 让连接更简单</p>
</div>
<!-- 内容 -->
<div class="content">
<div class="item-box" v-for="(item, index) in baseData" :key="index">
<!-- 标题 -->
<div class="item-title">
{{ item.title }}
</div>
<!-- 介绍 -->
<div class="item-description">
{{ item.description }}
</div>
<!-- 内容 -->
<div class="item-content">
{{ item.content }}
</div>
<div class="icon-box">
</div>
<!-- 价格 -->
<div class="item-price">
<span class="price-icon"></span> <span class="price">{{ item.price }}</span> /
</div>
<!-- 按钮 -->
<div class="item-button">
<div class="item-button-text">立即咨询</div>
</div>
</div>
</div>
</div>
<!-- 合作机构 -->
<div class="partner">
<div class="text">
<p class="text-top">合作伙伴</p>
<p class="text-btm">Partners</p>
</div>
<!-- 滚动合作伙伴内容 -->
<div class="partner-scroll">
<div class="logo-scroll-wrapper">
<div class="logo-scroll-container">
<div v-for="(image, index) in duplicatedImages" :key="index" class="logo-item">
<img :src="image" />
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { reqHotProduct } from '@/api/H5/index.js'
export default {
data() {
return {
baseData: [],
journeyData: [],
latitude: [],
images: [
require('../images/top/img.png'),
require('../images/top/img_1.png'),
require('../images/top/img_2.png'),
require('../images/top/img_3.png'),
require('../images/top/img_4.png'),
require('../images/top/img_5.png'),
require('../images/top/img_6.png'),
require('../images/top/img_8.png'),
require('../images/top/img_9.png')
]
}
},
computed: {
duplicatedImages() {
return [...this.images, ...this.images]
}
},
created() {
this.getHotProduct()
},
methods: {
async getHotProduct() {
const res = await reqHotProduct()
console.log('数据', res);
if (res.status == true) {
this.baseData = res.data.base
this.journeyData = res.data.suan
this.latitude = res.data.net
}
}
}
}
</script>
<style lang="less" scoped>
* {
margin: 0;
padding: 0;
font-size: 16px;
box-sizing: border-box;
}
.main-page {
width: 100%;
height: 100%;
background-color: #f8fafd;
}
.top-box {
width: 100%;
height: 50vh;
background: url("../images/banner.png") no-repeat;
background-size: 100% 100%;
display: flex;
align-items: center;
}
.title {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #222f60;
.name {
background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
font-size: .55rem;
}
.title-top {
font-size: .55rem;
}
.title-btm {
font-size: .26rem;
margin-top: .98rem;
}
}
//
.text {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: .14rem 0;
width: 100%;
.text-top {
font-size: .26rem;
color: #000;
}
.text-btm {
margin-top: .14rem;
font-size: .18rem;
color: #707070;
}
}
//
.base-box,
.journey-box,
.latitude-box {
width: 100%;
display: flex;
flex-direction: column;
padding: 0.3rem 0.4rem;
.content {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 0.2rem;
.item-box {
width: 48%;
background-color: #fff;
border-radius: 0.1rem;
padding: 0.2rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
margin-bottom: 0.2rem;
display: flex;
flex-direction: column;
.item-title {
font-size: 0.2rem;
font-weight: bold;
color: #333;
margin-bottom: 0.1rem;
}
.item-description {
font-size: 0.14rem;
color: #666;
margin-bottom: 0.15rem;
}
.item-content {
font-size: 0.14rem;
color: #333;
line-height: 1.4;
flex: 1;
}
.item-price {
font-size: 0.1rem;
font-weight: bold;
color: #d4d6e1;
margin: 0.15rem 0;
.price {
font-size: .22rem;
color: #f52220;
}
.price-icon {
color: #f52220;
font-size: .1rem;
}
}
.item-button {
background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%);
border-radius: 0.04rem;
padding: 0.08rem 0;
text-align: center;
.item-button-text {
color: #fff;
font-size: 0.14rem;
}
}
}
}
}
//
.partner {
background-color: #f8f9fd;
width: 100%;
display: flex;
flex-direction: column;
padding: 0.3rem 0.4rem;
.partner-scroll {
width: 100%;
max-width: 14rem;
margin: 0 auto;
padding: .15rem;
background: #fff;
border-radius: .16rem;
margin-bottom: .3rem;
.logo-scroll-wrapper {
width: 100%;
height: .85rem;
overflow: hidden;
position: relative;
}
.logo-scroll-container {
display: flex;
height: 100%;
animation: scroll 15s linear infinite;
white-space: nowrap;
}
.logo-item {
flex: 0 0 auto;
width: 1.68rem;
height: 100%;
margin-right: .2rem;
background: #fff;
border-radius: .04rem;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
}
}
@keyframes scroll {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
</style>

View File

@ -189,7 +189,7 @@
</el-table-column>
<el-table-column align="center" header-align="center" label="订单原价" width="140">
<template slot-scope="scope">
<div class="cell-content">{{ scope.row.list_price }}</div>
<div class="cell-content">{{ scope.row.origin_amout }}</div>
</template>
</el-table-column>
<el-table-column align="center" header-align="center" label="订单折后价" width="140">

View File

@ -353,7 +353,7 @@ export default Vue.extend({
color: #1b5bff;
}
}
/deep/.el-pagination{
/deep/.el-pagination{
text-align: center !important;
}
</style>

View File

@ -38,7 +38,7 @@
@mouseenter="selectSecondary(secondary)"
@click="handleSecondaryClick(secondary)">
{{ secondary.second_level_name }}
<span v-if="secondary.thirdClassification && secondary.thirdClassification.length > 0" class="item-arrow"></span>
<!-- <span v-if="secondary.thirdClassification && secondary.thirdClassification.length > 0" class="item-arrow"></span> -->
</div>
</div>

View File

@ -341,10 +341,10 @@ export default {
align-items: center;
}
.banner-box {
height: 700px!important;
height: 600px!important;
background: url('./img/tt-banner.png') no-repeat ;
width: 100%;
padding-bottom: 200px;
// padding-bottom: 10px;
.conter {
width: 700px;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,687 @@
.mianBox {
display: flex;
justify-content: space-between;
min-height: 100vh;
gap: 16px;
padding-bottom: 20px;
padding-bottom: 40px;
}
.left {
width: 30%;
min-width: 320px;
height: 100vh;
}
.left-card {
display: flex;
flex-direction: column;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.06);
}
.left-card /deep/ .el-card__body {
padding: 20px;
flex: 1;
height: 100%;
}
.right {
flex: 1;
min-width: 0;
}
.toptit {
font-size: 16px;
font-weight: 600;
color: #303133;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px solid #e4e7ed;
}
.search {
margin-bottom: 20px;
}
.search /deep/ .el-input-group__append {
background-color: #409eff;
border-color: #409eff;
}
.search /deep/ .el-input-group__append .el-button {
color: white;
}
.search /deep/ .el-input-group__append .el-button:hover {
background-color: #66b1ff;
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding: 6px 8px;
border-radius: 4px;
transition: background-color 0.2s;
}
.custom-tree-node:hover {
background-color: #f5f7fa;
}
.custom-tree-node .el-button {
padding: 4px;
margin-left: 4px;
}
.custom-tree-node .el-button i {
font-size: 12px;
}
.custom-tree-node .el-button:hover {
background-color: rgba(64, 158, 255, 0.1);
border-radius: 50%;
}
.tree-container {
overflow-y: auto;
border: 1px solid #e4e7ed;
border-radius: 6px;
padding: 12px;
background-color: #fafafa;
}
.tree-container /deep/ .el-tree .el-tree-node .el-tree-node__content {
height: 36px;
}
.tree-container /deep/ .el-tree .el-tree-node .el-tree-node__content:hover {
background-color: #f0f7ff;
}
.tree-container /deep/ .el-tree .el-tree-node.is-current > .el-tree-node__content {
background-color: #ecf5ff;
color: #409eff;
font-weight: 500;
}
.add-menu-box {
display: flex;
align-items: center;
justify-content: center;
border: 2px dashed #c0c4cc;
border-radius: 6px;
padding: 12px;
text-align: center;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
margin-top: 20px;
background-color: #fafafa;
color: #606266;
}
.add-menu-box:hover {
border-color: #409eff;
background-color: #f0f7ff;
color: #409eff;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.1);
}
.add-menu-box .add-icon {
font-size: 20px;
font-weight: 600;
margin-right: 8px;
line-height: 1;
}
.add-menu-box div {
font-size: 14px;
font-weight: 500;
}
.product-card {
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.06);
margin-bottom: 20px;
}
.product-card /deep/ .el-card__body {
padding: 24px;
}
.rigtop {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 24px;
flex-wrap: wrap;
gap: 16px;
}
.rigtop-left {
flex: 1;
min-width: 0;
}
.breadcrumb {
margin-top: 8px;
}
.breadcrumb /deep/ .el-breadcrumb__inner {
color: #909399;
font-size: 13px;
}
.breadcrumb /deep/ .el-breadcrumb__inner:hover {
color: #409eff;
}
.breadcrumb /deep/ .el-breadcrumb__separator {
color: #c0c4cc;
}
.current-menu {
font-size: 20px;
font-weight: 600;
color: #303133;
line-height: 1.4;
}
.add-product-btn .el-button {
border-radius: 6px;
padding: 10px 20px;
font-weight: 500;
}
.add-product-btn .el-button i {
margin-right: 4px;
}
.product-table {
margin-bottom: 24px;
}
.product-table /deep/ .el-table {
border-radius: 6px;
overflow: hidden;
border: 1px solid #ebeef5;
}
.product-table /deep/ .el-table th {
background-color: #f5f7fa;
color: #303133;
font-weight: 600;
height: 48px;
}
.product-table /deep/ .el-table td {
padding: 16px 0;
}
.product-table /deep/ .el-table .el-table__row {
cursor: pointer;
transition: background-color 0.2s;
}
.product-table /deep/ .el-table .el-table__row:hover {
background-color: #f5f7fa;
}
.product-table /deep/ .el-table .el-table__row.current-row {
background-color: #ecf5ff;
}
.product-info .product-name {
font-size: 14px;
font-weight: 600;
color: #303133;
margin-bottom: 6px;
line-height: 1.4;
}
.product-info .product-desc {
font-size: 12px;
color: #909399;
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.product-tags {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.product-tags .el-tag {
border-radius: 4px;
padding: 0 8px;
height: 24px;
line-height: 22px;
font-size: 12px;
}
.product-tags .el-tag--info {
background-color: #f4f4f5;
border-color: #e9e9eb;
color: #909399;
}
.action-buttons {
display: flex;
gap: 8px;
}
.action-buttons .el-button {
min-width: 60px;
padding: 7px 15px;
border-radius: 4px;
font-size: 12px;
}
.action-buttons .el-button--danger {
background-color: #fef0f0;
border-color: #fbc4c4;
color: #f56c6c;
}
.action-buttons .el-button--danger:hover {
background-color: #f56c6c;
color: white;
}
.pagination {
display: flex;
justify-content: flex-end;
margin-top: 24px;
padding-top: 24px;
border-top: 1px solid #ebeef5;
}
.pagination /deep/ .el-pagination .el-pagination__sizes,
.pagination /deep/ .el-pagination .el-pagination__jump {
margin-left: 16px;
}
.pagination /deep/ .el-pagination .btn-prev,
.pagination /deep/ .el-pagination .btn-next {
border-radius: 4px;
border: 1px solid #d8dce5;
}
.pagination /deep/ .el-pagination .el-pager li {
border-radius: 4px;
border: 1px solid #d8dce5;
margin: 0 4px;
}
.pagination /deep/ .el-pagination .el-pager li.active {
background-color: #409eff;
color: white;
border-color: #409eff;
}
.card-top {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 16px;
border-bottom: 1px solid #e4e7ed;
margin-bottom: 24px;
}
.card-top .top-tit span:first-child {
font-size: 18px;
font-weight: 600;
color: #303133;
display: block;
margin-bottom: 4px;
}
.card-top .top-tit span:last-child {
font-size: 13px;
color: #909399;
}
.card-top .top-btn .el-button {
border-radius: 6px;
padding: 10px 24px;
font-weight: 500;
}
.card-content {
width: 100%;
}
.card-content .content-tab {
width: 100%;
}
.card-content .content-tab .tab-top {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 16px;
}
.card-content .content-tab .tab-content {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-bottom: 24px;
}
.card-content .content-tab .tab-content .tab-item {
padding: 10px 24px;
border-radius: 20px;
background-color: #f5f7fa;
color: #606266;
font-size: 14px;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
border: 1px solid transparent;
}
.card-content .content-tab .tab-content .tab-item:hover {
background-color: #e4e7ed;
transform: translateY(-1px);
}
.card-content .content-tab .tab-content .tab-item.active {
background-color: #409eff;
color: white;
font-weight: 500;
border-color: #409eff;
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
}
.product-detail-card {
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.06);
}
.product-detail-card /deep/ .el-card__body {
padding: 24px;
}
.product-detail-card .product-basic-info {
padding: 20px;
background-color: #fafafa;
border-radius: 6px;
border: 1px solid #ebeef5;
}
.product-detail-card .product-basic-info /deep/ .el-form-item {
margin-bottom: 20px;
}
.product-detail-card .product-basic-info /deep/ .el-form-item:last-child {
margin-bottom: 0;
}
.product-detail-card .product-basic-info /deep/ .el-form-item .el-form-item__label {
color: #606266;
font-weight: 500;
}
.product-detail-card .product-basic-info /deep/ .el-form-item .el-input .el-input__inner,
.product-detail-card .product-basic-info /deep/ .el-form-item .el-textarea .el-input__inner,
.product-detail-card .product-basic-info /deep/ .el-form-item .el-input .el-textarea__inner,
.product-detail-card .product-basic-info /deep/ .el-form-item .el-textarea .el-textarea__inner {
border-radius: 4px;
}
.product-detail-card .product-basic-info /deep/ .el-form-item .el-input .el-input__inner:focus,
.product-detail-card .product-basic-info /deep/ .el-form-item .el-textarea .el-input__inner:focus,
.product-detail-card .product-basic-info /deep/ .el-form-item .el-input .el-textarea__inner:focus,
.product-detail-card .product-basic-info /deep/ .el-form-item .el-textarea .el-textarea__inner:focus {
border-color: #409eff;
}
.product-detail-card .product-basic-info /deep/ .el-form-item .el-input-number .el-input-number__decrease,
.product-detail-card .product-basic-info /deep/ .el-form-item .el-input-number .el-input-number__increase {
border-radius: 4px 0 0 4px;
}
.product-detail-card .product-basic-info /deep/ .el-form-item .el-input-number .el-input__inner {
border-radius: 0 4px 4px 0;
}
.product-detail-card .product-detail-content {
margin-top: 16px;
}
.product-detail-card .product-detail-content .editor-container /deep/ .el-textarea .el-textarea__inner {
border-radius: 6px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 14px;
line-height: 1.6;
padding: 16px;
border: 1px solid #dcdfe6;
}
.product-detail-card .product-detail-content .editor-container /deep/ .el-textarea .el-textarea__inner:focus {
border-color: #409eff;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}
.product-detail-card .product-detail-content .editor-container /deep/ .el-textarea .el-textarea__inner::placeholder {
color: #c0c4cc;
}
.empty-detail {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80px 40px;
text-align: center;
}
.empty-detail .empty-icon {
font-size: 64px;
margin-bottom: 24px;
opacity: 0.3;
animation: float 3s ease-in-out infinite;
}
.empty-detail .empty-text {
color: #909399;
font-size: 16px;
line-height: 1.6;
max-width: 300px;
}
/deep/ .el-dialog {
border-radius: 8px;
}
/deep/ .el-dialog .el-dialog__header {
padding: 20px 24px 10px;
border-bottom: 1px solid #e4e7ed;
}
/deep/ .el-dialog .el-dialog__header .el-dialog__title {
font-size: 18px;
font-weight: 600;
color: #303133;
}
/deep/ .el-dialog .el-dialog__body {
padding: 20px 24px;
}
/deep/ .el-dialog .el-dialog__body .el-form-item {
margin-bottom: 20px;
}
/deep/ .el-dialog .el-dialog__body .el-form-item:last-child {
margin-bottom: 0;
}
/deep/ .el-dialog .el-dialog__footer {
padding: 10px 24px 20px;
border-top: 1px solid #e4e7ed;
}
/deep/ .el-dialog .el-dialog__footer .dialog-footer .el-button {
border-radius: 6px;
padding: 10px 20px;
min-width: 80px;
}
/deep/ .el-dialog .el-dialog__footer .dialog-footer .el-button--primary {
font-weight: 500;
}
@keyframes float {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
}
@media (max-width: 1200px) {
.mianBox {
flex-direction: column;
}
.left,
.right {
width: 100%;
}
.left {
margin-bottom: 16px;
}
.tree-container {
height: 300px;
}
}
.pagination-controls {
width: 100%;
display: flex;
justify-content: center;
}
@media (max-width: 768px) {
.rigtop {
flex-direction: column;
align-items: stretch;
}
.add-product-btn {
align-self: flex-start;
}
.tab-content {
flex-direction: column;
}
.tab-content .tab-item {
width: 100%;
text-align: center;
}
.action-buttons {
flex-direction: column;
gap: 4px;
}
}
.content-form {
padding: 20px;
}
.content-form .el-form-item {
margin-bottom: 20px;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #ebeef5;
}
.section-header h3 {
margin: 0;
font-size: 18px;
color: #303133;
}
.form-list {
margin-top: 20px;
}
.form-item {
padding: 20px;
margin-bottom: 20px;
border: 1px solid #ebeef5;
border-radius: 4px;
background-color: #fafafa;
}
.form-item.main-form-item {
background-color: #f5f7fa;
}
.form-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px dashed #ebeef5;
color: #409eff;
}
.form-actions {
text-align: right;
margin-top: 10px;
padding-top: 10px;
border-top: 1px dashed #ebeef5;
}
.form-actions .el-button {
padding: 0 10px;
}
.saved-list {
margin-bottom: 30px;
}
.list-item {
padding: 15px;
margin-bottom: 15px;
border: 1px solid #e4e7ed;
border-radius: 4px;
background-color: #fff;
}
.list-item.main-item {
background-color: #f8f9fa;
}
.list-item .item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #f0f0f0;
}
.list-item .item-header .item-title {
font-weight: bold;
color: #606266;
}
.list-item .item-content {
display: flex;
align-items: flex-start;
}
.list-item .item-content .item-image {
margin-right: 15px;
flex-shrink: 0;
}
.list-item .item-content .item-details {
flex: 1;
}
.list-item .item-content .item-details div {
margin-bottom: 5px;
}
.list-item .item-content .item-details div strong {
color: #606266;
margin-right: 5px;
}
.detail-section {
margin-top: 15px;
padding-top: 15px;
border-top: 1px dashed #e4e7ed;
}
.detail-list .detail-item {
padding: 10px;
margin-bottom: 10px;
background-color: #fff;
border: 1px solid #f0f0f0;
border-radius: 4px;
}
.detail-list .detail-item .detail-title {
font-weight: bold;
margin-bottom: 8px;
color: #67c23a;
}
.detail-list .detail-item .detail-content {
margin-bottom: 8px;
}
.detail-list .detail-item .detail-content div {
margin-bottom: 3px;
}
.detail-list .detail-item .detail-content div strong {
color: #909399;
margin-right: 5px;
}
.detail-list .detail-item .detail-actions {
text-align: right;
}
.detail-form-section {
margin-top: 15px;
padding: 15px;
background-color: #f9f9f9;
border-radius: 4px;
border: 1px dashed #dcdfe6;
}
.detail-form-item {
margin-bottom: 15px;
padding: 15px;
background-color: #fff;
border-radius: 4px;
border: 1px solid #ebeef5;
}
.add-detail-btn {
text-align: center;
margin-top: 10px;
}
.main-product-form {
padding: 20px;
margin-bottom: 30px;
border: 1px solid #ebeef5;
border-radius: 4px;
background-color: #fafafa;
}
.card-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #ebeef5;
}
.card-top .top-tit {
display: flex;
flex-direction: column;
}
.card-top .top-tit span:first-child {
font-size: 18px;
font-weight: bold;
margin-bottom: 5px;
}
.card-top .top-tit span:last-child {
font-size: 14px;
color: #409eff;
}
.no-product-selected {
padding: 80px 20px;
text-align: center;
}
.no-product-selected .empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 200px;
}
.product-table /deep/ .el-table__row {
cursor: pointer;
}
.product-table /deep/ .el-table__row:hover {
background-color: #f5f7fa;
}
.product-table /deep/ .el-table__row.current-row {
background-color: #ecf5ff;
}

View File

@ -0,0 +1,868 @@
.mianBox {
display: flex;
justify-content: space-between;
min-height: 100vh;
// min-height: 600px;
gap: 16px;
padding-bottom: 20px;
padding-bottom: 40px;
// box-sizing: border-box
}
.left {
width: 30%;
min-width: 320px;
height: 100vh;
}
.left-card {
// height: 100%;
display: flex;
flex-direction: column;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.06);
/deep/ .el-card__body {
padding: 20px;
flex: 1;
height: 100%;
}
}
.right {
flex: 1;
min-width: 0; // 防止flex溢出
}
.toptit {
font-size: 16px;
font-weight: 600;
color: #303133;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px solid #e4e7ed;
}
.search {
margin-bottom: 20px;
/deep/ .el-input-group__append {
background-color: #409eff;
border-color: #409eff;
.el-button {
color: white;
&:hover {
background-color: #66b1ff;
}
}
}
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding: 6px 8px;
border-radius: 4px;
transition: background-color 0.2s;
&:hover {
background-color: #f5f7fa;
}
.el-button {
padding: 4px;
margin-left: 4px;
i {
font-size: 12px;
}
&:hover {
background-color: rgba(64, 158, 255, 0.1);
border-radius: 50%;
}
}
}
.tree-container {
// height: 400px;
overflow-y: auto;
border: 1px solid #e4e7ed;
border-radius: 6px;
padding: 12px;
background-color: #fafafa;
/deep/ .el-tree {
.el-tree-node {
.el-tree-node__content {
height: 36px;
&:hover {
background-color: #f0f7ff;
}
}
&.is-current {
> .el-tree-node__content {
background-color: #ecf5ff;
color: #409eff;
font-weight: 500;
}
}
}
}
}
.add-menu-box {
display: flex;
align-items: center;
justify-content: center;
border: 2px dashed #c0c4cc;
border-radius: 6px;
padding: 12px;
text-align: center;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
margin-top: 20px;
background-color: #fafafa;
color: #606266;
&:hover {
border-color: #409eff;
background-color: #f0f7ff;
color: #409eff;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.1);
}
.add-icon {
font-size: 20px;
font-weight: 600;
margin-right: 8px;
line-height: 1;
}
div {
font-size: 14px;
font-weight: 500;
}
}
// 右侧产品卡片样式
.product-card {
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.06);
margin-bottom: 20px;
/deep/ .el-card__body {
padding: 24px;
}
}
.rigtop {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 24px;
flex-wrap: wrap;
gap: 16px;
}
.rigtop-left {
flex: 1;
min-width: 0;
}
.breadcrumb {
margin-top: 8px;
/deep/ .el-breadcrumb__inner {
color: #909399;
font-size: 13px;
&:hover {
color: #409eff;
}
}
/deep/ .el-breadcrumb__separator {
color: #c0c4cc;
}
}
.current-menu {
font-size: 20px;
font-weight: 600;
color: #303133;
line-height: 1.4;
}
.add-product-btn {
.el-button {
border-radius: 6px;
padding: 10px 20px;
font-weight: 500;
i {
margin-right: 4px;
}
}
}
// 产品表格样式
.product-table {
margin-bottom: 24px;
/deep/ .el-table {
border-radius: 6px;
overflow: hidden;
border: 1px solid #ebeef5;
th {
background-color: #f5f7fa;
color: #303133;
font-weight: 600;
height: 48px;
}
td {
padding: 16px 0;
}
.el-table__row {
cursor: pointer;
transition: background-color 0.2s;
&:hover {
background-color: #f5f7fa;
}
&.current-row {
background-color: #ecf5ff;
}
}
}
}
.product-info {
.product-name {
font-size: 14px;
font-weight: 600;
color: #303133;
margin-bottom: 6px;
line-height: 1.4;
}
.product-desc {
font-size: 12px;
color: #909399;
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
.product-tags {
display: flex;
flex-wrap: wrap;
gap: 4px;
.el-tag {
border-radius: 4px;
padding: 0 8px;
height: 24px;
line-height: 22px;
font-size: 12px;
&--info {
background-color: #f4f4f5;
border-color: #e9e9eb;
color: #909399;
}
}
}
// 操作按钮样式
.action-buttons {
display: flex;
gap: 8px;
.el-button {
min-width: 60px;
padding: 7px 15px;
border-radius: 4px;
font-size: 12px;
&--danger {
background-color: #fef0f0;
border-color: #fbc4c4;
color: #f56c6c;
&:hover {
background-color: #f56c6c;
color: white;
}
}
}
}
// 分页样式
.pagination {
display: flex;
justify-content: flex-end;
margin-top: 24px;
padding-top: 24px;
border-top: 1px solid #ebeef5;
/deep/ .el-pagination {
.el-pagination__sizes,
.el-pagination__jump {
margin-left: 16px;
}
.btn-prev,
.btn-next {
border-radius: 4px;
border: 1px solid #d8dce5;
}
.el-pager {
li {
border-radius: 4px;
border: 1px solid #d8dce5;
margin: 0 4px;
&.active {
background-color: #409eff;
color: white;
border-color: #409eff;
}
}
}
}
}
.card-top {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 16px;
border-bottom: 1px solid #e4e7ed;
margin-bottom: 24px;
.top-tit {
span:first-child {
font-size: 18px;
font-weight: 600;
color: #303133;
display: block;
margin-bottom: 4px;
}
span:last-child {
font-size: 13px;
color: #909399;
}
}
.top-btn {
.el-button {
border-radius: 6px;
padding: 10px 24px;
font-weight: 500;
}
}
}
.card-content {
width: 100%;
.content-tab {
width: 100%;
.tab-top {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 16px;
}
.tab-content {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-bottom: 24px;
.tab-item {
padding: 10px 24px;
border-radius: 20px;
background-color: #f5f7fa;
color: #606266;
font-size: 14px;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
border: 1px solid transparent;
&:hover {
background-color: #e4e7ed;
transform: translateY(-1px);
}
&.active {
background-color: #409eff;
color: white;
font-weight: 500;
border-color: #409eff;
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
}
}
}
}
}
// 产品详情卡片样式
.product-detail-card {
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.06);
/deep/ .el-card__body {
padding: 24px;
}
.product-basic-info {
padding: 20px;
background-color: #fafafa;
border-radius: 6px;
border: 1px solid #ebeef5;
/deep/ .el-form-item {
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
}
.el-form-item__label {
color: #606266;
font-weight: 500;
}
.el-input,
.el-textarea {
.el-input__inner,
.el-textarea__inner {
border-radius: 4px;
&:focus {
border-color: #409eff;
}
}
}
.el-input-number {
.el-input-number__decrease,
.el-input-number__increase {
border-radius: 4px 0 0 4px;
}
.el-input__inner {
border-radius: 0 4px 4px 0;
}
}
}
}
.product-detail-content {
margin-top: 16px;
.editor-container {
/deep/ .el-textarea {
.el-textarea__inner {
border-radius: 6px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 14px;
line-height: 1.6;
padding: 16px;
border: 1px solid #dcdfe6;
&:focus {
border-color: #409eff;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}
&::placeholder {
color: #c0c4cc;
}
}
}
}
}
}
.empty-detail {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80px 40px;
text-align: center;
.empty-icon {
font-size: 64px;
margin-bottom: 24px;
opacity: 0.3;
animation: float 3s ease-in-out infinite;
}
.empty-text {
color: #909399;
font-size: 16px;
line-height: 1.6;
max-width: 300px;
}
}
// 对话框样式优化
/deep/ .el-dialog {
border-radius: 8px;
.el-dialog__header {
padding: 20px 24px 10px;
border-bottom: 1px solid #e4e7ed;
.el-dialog__title {
font-size: 18px;
font-weight: 600;
color: #303133;
}
}
.el-dialog__body {
padding: 20px 24px;
.el-form-item {
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
}
}
}
.el-dialog__footer {
padding: 10px 24px 20px;
border-top: 1px solid #e4e7ed;
.dialog-footer {
.el-button {
border-radius: 6px;
padding: 10px 20px;
min-width: 80px;
&--primary {
font-weight: 500;
}
}
}
}
}
// 添加动画效果
@keyframes float {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
}
// 响应式调整
@media (max-width: 1200px) {
.mianBox {
flex-direction: column;
}
.left,
.right {
width: 100%;
}
.left {
margin-bottom: 16px;
}
.tree-container {
height: 300px;
}
}
.pagination-controls{
width: 100%;
display: flex;
justify-content: center;
}
@media (max-width: 768px) {
.rigtop {
flex-direction: column;
align-items: stretch;
}
.add-product-btn {
align-self: flex-start;
}
.tab-content {
flex-direction: column;
.tab-item {
width: 100%;
text-align: center;
}
}
.action-buttons {
flex-direction: column;
gap: 4px;
}
}
// 详情:添加样式
.content-form {
padding: 20px;
.el-form-item {
margin-bottom: 20px;
}
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #ebeef5;
h3 {
margin: 0;
font-size: 18px;
color: #303133;
}
}
.form-list {
margin-top: 20px;
}
.form-item {
padding: 20px;
margin-bottom: 20px;
border: 1px solid #ebeef5;
border-radius: 4px;
background-color: #fafafa;
&.main-form-item {
background-color: #f5f7fa;
}
}
.form-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px dashed #ebeef5;
color: #409eff;
}
.form-actions {
text-align: right;
margin-top: 10px;
padding-top: 10px;
border-top: 1px dashed #ebeef5;
.el-button {
padding: 0 10px;
}
}
.saved-list {
margin-bottom: 30px;
}
.list-item {
padding: 15px;
margin-bottom: 15px;
border: 1px solid #e4e7ed;
border-radius: 4px;
background-color: #fff;
&.main-item {
background-color: #f8f9fa;
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #f0f0f0;
.item-title {
font-weight: bold;
color: #606266;
}
}
.item-content {
display: flex;
align-items: flex-start;
.item-image {
margin-right: 15px;
flex-shrink: 0;
}
.item-details {
flex: 1;
div {
margin-bottom: 5px;
strong {
color: #606266;
margin-right: 5px;
}
}
}
}
}
.detail-section {
margin-top: 15px;
padding-top: 15px;
border-top: 1px dashed #e4e7ed;
}
.detail-list {
.detail-item {
padding: 10px;
margin-bottom: 10px;
background-color: #fff;
border: 1px solid #f0f0f0;
border-radius: 4px;
.detail-title {
font-weight: bold;
margin-bottom: 8px;
color: #67c23a;
}
.detail-content {
margin-bottom: 8px;
div {
margin-bottom: 3px;
strong {
color: #909399;
margin-right: 5px;
}
}
}
.detail-actions {
text-align: right;
}
}
}
.detail-form-section {
margin-top: 15px;
padding: 15px;
background-color: #f9f9f9;
border-radius: 4px;
border: 1px dashed #dcdfe6;
}
.detail-form-item {
margin-bottom: 15px;
padding: 15px;
background-color: #fff;
border-radius: 4px;
border: 1px solid #ebeef5;
}
.add-detail-btn {
text-align: center;
margin-top: 10px;
}
.main-product-form {
padding: 20px;
margin-bottom: 30px;
border: 1px solid #ebeef5;
border-radius: 4px;
background-color: #fafafa;
}
.card-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #ebeef5;
.top-tit {
display: flex;
flex-direction: column;
span:first-child {
font-size: 18px;
font-weight: bold;
margin-bottom: 5px;
}
span:last-child {
font-size: 14px;
color: #409eff;
}
}
}
.no-product-selected {
padding: 80px 20px;
text-align: center;
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 200px;
}
}
// 详情:为产品表格行添加悬停效果
.product-table {
/deep/ .el-table__row {
cursor: pointer;
&:hover {
background-color: #f5f7fa;
}
&.current-row {
background-color: #ecf5ff;
}
}
}

View File

@ -81,7 +81,7 @@
</template>
<script>
import { reqNavList } from "@/api/newHome";
import { reqNavList, reqNewHomeSync, reqNewHomeFestival } from "@/api/newHome";
export default {
name: "ProductServicePage",
@ -125,6 +125,11 @@ export default {
);
return subItem && subItem.thrMenu ? subItem.thrMenu : [];
},
//
loginState() {
const userId = sessionStorage.getItem('userId');
return userId !== null && userId !== 'null' && userId !== '';
}
},
async mounted() {
@ -134,13 +139,11 @@ export default {
methods: {
// key
getSubItemKey(subItem) {
// 使ID
return `${subItem.id}_${this.activeCategory}_${subItem.secTitle}`;
},
// key
getThirdItemKey(thirdItem) {
// 使IDID
return `${thirdItem.id}_${this.activeSubId}_${thirdItem.thrTitle}`;
},
@ -326,9 +329,35 @@ export default {
return descriptions[product.name] || '专业的云服务产品,提供稳定可靠的服务';
},
//
async handleAliyunProductClick() {
try {
//
const syncResponse = await reqNewHomeSync();
if (!syncResponse.status) {
this.$message.warning(syncResponse.msg || '同步失败,请稍后重试');
return;
}
//
const festivalResponse = await reqNewHomeFestival();
if (festivalResponse.status && festivalResponse.data) {
window.open(festivalResponse.data);
} else {
this.$message.warning(festivalResponse.msg || '获取跳转链接失败');
}
} catch (error) {
console.error('阿里云跳转失败:', error);
this.$message.error('网络错误,请稍后重试');
}
},
//
handleProductClick(product) {
//
async handleProductClick(product) {
console.log('点击产品:', product);
const userId = sessionStorage.getItem('userId');
if (product.type === '百度云') {
@ -354,8 +383,9 @@ export default {
});
}
} else if (product.type === '阿里云') {
if (userId) {
window.open(product.url);
await this.handleAliyunProductClick();
} else {
this.$router.push({
path: "/login",
@ -397,7 +427,6 @@ export default {
</script>
<style lang="less" scoped>
/* 样式保持不变 */
.product-service-page {
margin: 0 auto;
padding: 24px;
@ -533,112 +562,112 @@ export default {
.product-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 12px;
align-items: flex-start;
margin-bottom: 12px;
.product-name {
font-size: 16px;
font-weight: 600;
color: #1a1a1a;
margin: 0;
flex: 1;
}
.product-name {
font-size: 16px;
font-weight: 600;
color: #1a1a1a;
margin: 0;
flex: 1;
}
.discount-badge {
background: linear-gradient(135deg, #ff6b6b, #ff4757);
color: #fff;
font-size: 12px;
padding: 4px 8px;
border-radius: 4px;
font-weight: 600;
margin-left: 8px;
}
}
.discount-badge {
background: linear-gradient(135deg, #ff6b6b, #ff4757);
color: #fff;
font-size: 12px;
padding: 4px 8px;
border-radius: 4px;
font-weight: 600;
margin-left: 8px;
}
}
.product-desc {
font-size: 14px;
color: #666;
line-height: 1.5;
margin-bottom: 16px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.product-desc {
font-size: 14px;
color: #666;
line-height: 1.5;
margin-bottom: 16px;
display: -webkit-box;
// -webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.product-footer {
display: flex;
justify-content: space-between;
align-items: center;
.product-footer {
display: flex;
justify-content: space-between;
align-items: center;
.product-type {
font-size: 12px;
color: #999;
background: #f8f9fa;
padding: 4px 8px;
border-radius: 4px;
}
.product-type {
font-size: 12px;
color: #999;
background: #f8f9fa;
padding: 4px 8px;
border-radius: 4px;
}
.detail-btn {
background: #1E6FFF;
color: #fff;
border: none;
padding: 6px 12px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
transition: background 0.3s;
.detail-btn {
background: #1E6FFF;
color: #fff;
border: none;
padding: 6px 12px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
transition: background 0.3s;
&:hover {
background: #0d5ae0;
&:hover {
background: #0d5ae0;
}
}
}
}
}
}
}
.empty-state {
text-align: center;
padding: 60px 20px;
.empty-state {
text-align: center;
padding: 60px 20px;
.empty-icon {
font-size: 48px;
margin-bottom: 16px;
}
.empty-icon {
font-size: 48px;
margin-bottom: 16px;
}
.empty-text {
color: #999;
font-size: 14px;
}
}
}
}
}
@media (max-width: 768px) {
.product-service-page {
padding: 16px;
.product-content {
flex-direction: column;
.menu-sidebar {
width: 100%;
order: 2;
}
.main-content {
order: 1;
&.full-width {
margin-left: 0;
.empty-text {
color: #999;
font-size: 14px;
}
}
}
}
}
.product-grid {
grid-template-columns: 1fr !important;
@media (max-width: 768px) {
.product-service-page {
padding: 16px;
.product-content {
flex-direction: column;
.menu-sidebar {
width: 100%;
order: 2;
}
.main-content {
order: 1;
&.full-width {
margin-left: 0;
}
}
}
.product-grid {
grid-template-columns: 1fr !important;
}
}
}
}
}
</style>

View File

@ -41,17 +41,28 @@
</el-tag>
</template>
</el-table-column>
<el-table-column prop="expiretime" label="到期时间" min-width="120">
<template slot-scope="scope">
<span class="time">{{ scope.row.expiretime }}</span>
</template>
</el-table-column>
<el-table-column prop="days" label="剩余天数" min-width="80">
<template slot-scope="scope">
<span :class="scope.row.days < 3 ? 'critical' : ''">{{ scope.row.days }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="80">
<template slot-scope="scope">
<el-button size="mini" type="primary" @click="goBaiDu(scope.row.list_url)">
续费
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
<!-- 右侧部分保持不变 -->
<div class="rightBox">
<div class="user card">
<div class="userImg">
@ -64,7 +75,8 @@
<p><i class="el-icon-phone"></i> 手机号: {{ userInfo.mobile }}</p>
<p><i class="el-icon-message"></i> 邮箱: {{ userInfo.email }}</p>
</div>
</div>· </div>
</div>
</div>
<div class="price card">
<div class="title">账户余额</div>
@ -128,8 +140,6 @@ export default Vue.extend({
},
viewList: [],
navList: [],
// data mybalance
mybalance: 0,
todoList: [
{ name: '待支付', count: 0 },
{ name: '待续费', count: 0 },
@ -137,14 +147,12 @@ export default Vue.extend({
{ name: '站内信', count: 0 }
],
messageCenterVisible: false,
//
navIcons: [icon1, icon2, icon3, icon4]
}
},
created() {
this.initMybalance();
this.getUnreadMsgCount();
this.getUnreadMsgCount();
this.fetchTodoCount();
},
mounted() {
@ -168,6 +176,16 @@ export default Vue.extend({
})
},
methods: {
goBaiDu(listUrl) {
this.$store.commit('setRedirectUrl', listUrl);
localStorage.setItem('redirectUrl', listUrl);
this.$router.push({
name: 'baiduProductShow',
params: {
listUrl: listUrl,
}
});
},
goBaidu(item) {
this.$store.commit('setRedirectUrl', item.url)
localStorage.setItem('redirectUrl', item.url)
@ -180,9 +198,7 @@ export default Vue.extend({
}
})
},
//
getNavIcon(index) {
// 使
const iconIndex = index % this.navIcons.length;
return this.navIcons[iconIndex];
},
@ -190,7 +206,8 @@ export default Vue.extend({
async initMybalance() {
const res = await editReachargelogAPI()
if (res.status) {
this.mybalance = res.data
// user 使 'SETMYBANLANCE' 'user/setMybalance'
this.$store.commit('SETMYBANLANCE', res.data)
}
},
async getUnreadMsgCount() {
@ -209,12 +226,10 @@ export default Vue.extend({
this.$message.error('获取未读消息失败');
}
},
//
async fetchTodoCount() {
try {
const res = await todoCount();
if (res.status && res.data) {
//
this.todoList = this.todoList.map(item => {
switch(item.name) {
case '待支付':
@ -240,27 +255,17 @@ export default Vue.extend({
this.openMessageCenter();
} else {
let query = {};
switch(todoName) {
case '待支付':
query = {
filterType: 'processing'
};
query = { filterType: 'processing' };
break;
case '待续费':
query = {
filterType: 'pendingPayment'
};
query = { filterType: 'pendingPayment' };
break;
case '处理中':
query = {
filterType: 'processing'
};
query = { filterType: 'processing' };
break;
}
console.log(`跳转到资源概览,筛选类型: ${todoName}`, query);
this.$router.push({
path: '/orderManagement/orderManagement',
query: query
@ -367,7 +372,6 @@ export default Vue.extend({
justify-content: center;
min-width: 0;
//
.nav-icon {
width: 24px;
height: 24px;
@ -375,13 +379,6 @@ export default Vue.extend({
object-fit: contain;
}
//
// i {
// font-size: 24px;
// margin-bottom: 10px;
// color: #409eff;
// }
&:hover {
transform: translateY(-3px);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.2);

View File

@ -31,7 +31,8 @@ class BaiduSMS:
# 替换为您的百度短信签名名称
# self.signature_id = 'sms-sign-qQHYeC17077' # 开元云科技
# self.signature_id = 'sms-sign-BqOhYB33019' # 开元云
self.signature_id = 'sms-sign-LOShPq75464' # 开元云北京
# self.signature_id = 'sms-sign-LOShPq75464' # 开元云北京
self.signature_id = 'sms-sign-xQYUwp42637' # 开元云北京
# 短信模板类型映射键为业务类型值为对应模板ID
self.sms_types = {
"注册登录验证": "sms-tpl-123", # 示例模板ID