Compare commits

..

10 Commits
prod ... main

Author SHA1 Message Date
ping
b57a0267f4 update 2025-12-05 14:46:52 +08:00
hrx
a922670038 updata 2025-12-05 14:43:55 +08:00
hrx
b6630aa2d7 updata 2025-12-05 14:43:32 +08:00
ping
4832d14814 update 2025-12-04 16:28:46 +08:00
hrx
f487ed7a3e updata 2025-12-04 16:26:44 +08:00
hrx
48bf9e6461 updata 2025-12-04 16:26:16 +08:00
ping
3ab1e55f4c uupdate 2025-12-04 13:49:14 +08:00
ping
8506d3e091 update 2025-11-28 18:15:49 +08:00
ping
9e1997e3f3 Merge branch 'main' of https://git.opencomputing.cn/yumoqing/kboss 2025-11-27 18:00:22 +08:00
ping
0ce9836c0d update 2025-11-27 18:00:11 +08:00
25 changed files with 4173 additions and 173 deletions

View File

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

View File

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

View File

@ -21,6 +21,8 @@ async def baidu_query_by_expire_time(ns={}):
'msg': 'User not synchronized' 'msg': 'User not synchronized'
} }
ns['queryAccountId'] = baiduid ns['queryAccountId'] = baiduid
ns['pageNo'] = int(ns.get('pageno', 1))
ns['pageSize'] = int(ns.get('pagesize', 100))
method = 'POST' method = 'POST'
ns_format = '&'.join(['%s=%s' % (k, v) for k, v in ns.items()]) ns_format = '&'.join(['%s=%s' % (k, v) for k, v in ns.items()])
url = 'https://billing.baidubce.com/v1/resource/queryByExpireTime?%s' % ns_format 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: json=ns) as res:
data_ = await res.json() data_ = await res.json()
print('data_', data_) print('data_', data_)
if data_.get('pageSize'):
return { return {
'status': True, 'status': True,
'msg': 'get baidu resource expire time success', 'msg': 'get baidu resource expire time success',
'data': data_ 'data': data_
} }
else:
return {
'status': False,
'msg': 'get baidu resource expire time failed',
'data': data_
}
async def calculate_time_diff(time_str=None): async def calculate_time_diff(time_str=None):
# 将字符串时间转换为 datetime 对象 # 将字符串时间转换为 datetime 对象
@ -63,22 +72,46 @@ async def get_resource_expire_time(ns={}):
else: else:
userid = await get_user() userid = await get_user()
data = [] 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'): if baidu_resource_data.get('data'):
for baidubaidu_resource in baidu_resource_data['data']['result']: data_result = baidu_resource_data['data']['result'] if baidu_resource_data['data'].get('result') else []
baidu_r = { # if not data_result:
'id': uuid(), # return {
'name': baidubaidu_resource['serviceTypeName'], # 'status': True,
'instanceid': baidubaidu_resource['shortId'], # 'msg': '无资源数据',
'status': baidubaidu_resource['status'], # 'data': str(baidu_resource_data['data'])
'expiretime': await time_convert(baidubaidu_resource['expireTime']), # }
'days': await calculate_time_diff(baidubaidu_resource['expireTime']) for baidubaidu_resource in data_result:
} baidubaidu_resource['id'] = uuid()
data.append(baidu_r) 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 { return {
'status': True, 'status': True,
'msg': 'get resouce expire time success', '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) 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'): # if not ns_dic.get('menu_level') or not ns_dic.get('title'):
return { # return {
'status': False, # 'status': False,
'msg': 'menu_level and title are required' # '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 { return {
'status': False, 'status': False,
'msg': 'parent_id is required for menu_level > 1' '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对菜单进行排序 # 通过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 = {} menu_dict = {}
result = [] result = []

View File

@ -14,12 +14,12 @@ async def home_page_product_menu_update(ns={}):
ns_dic = {'id': ns.get('id')} ns_dic = {'id': ns.get('id')}
if ns.get('title'): if ns.get('title'):
ns_dic['title'] = 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') ns_dic['parent_id'] = ns.get('parent_id')
if ns.get('menu_level'): if ns.get('menu_level'):
ns_dic['menu_level'] = ns.get('menu_level') ns_dic['menu_level'] = ns.get('menu_level')
if ns.get('sort_order') is not None: if ns.get('sort_order'):
ns_dic['sort_order'] = ns.get('sort_order') ns_dic['sort_order'] = int(ns.get('sort_order'))
db = DBPools() db = DBPools()
async with db.sqlorContext('kboss') as sor: 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')} ns_dic = {'id': ns.get('id')}
if ns.get('name'): if ns.get('name'):
ns_dic['name'] = 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') ns_dic['description'] = ns.get('description')
if ns.get('label') is not None: if ns.get('label'):
ns_dic['label'] = 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') 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') 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') 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') 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') 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') ns_dic['sort_order'] = ns.get('sort_order')
if ns.get('menu_id'): if ns.get('menu_id'):
ns_dic['menu_id'] = ns.get('menu_id') ns_dic['menu_id'] = ns.get('menu_id')

Binary file not shown.

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

@ -1156,8 +1156,17 @@ export const asyncRoutes = [
path: "index", path: "index",
component: () => import("@/views/operation/menuMangement/index.vue"), component: () => import("@/views/operation/menuMangement/index.vue"),
name: "menuMangement", 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 = { 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) { generateRoutes({ commit, rootState }, params) {
console.log("ACTION generateRoutes - params:", params); console.log("ACTION generateRoutes - params:", params);
return new Promise((resolve) => { return new Promise((resolve) => {

View File

@ -38,7 +38,7 @@
@mouseenter="selectSecondary(secondary)" @mouseenter="selectSecondary(secondary)"
@click="handleSecondaryClick(secondary)"> @click="handleSecondaryClick(secondary)">
{{ secondary.second_level_name }} {{ 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>
</div> </div>

View File

@ -341,10 +341,10 @@ export default {
align-items: center; align-items: center;
} }
.banner-box { .banner-box {
height: 700px!important; height: 600px!important;
background: url('./img/tt-banner.png') no-repeat ; background: url('./img/tt-banner.png') no-repeat ;
width: 100%; width: 100%;
padding-bottom: 200px; // padding-bottom: 10px;
.conter { .conter {
width: 700px; 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> </template>
<script> <script>
import { reqNavList } from "@/api/newHome"; import { reqNavList, reqNewHomeSync, reqNewHomeFestival } from "@/api/newHome";
export default { export default {
name: "ProductServicePage", name: "ProductServicePage",
@ -125,6 +125,11 @@ export default {
); );
return subItem && subItem.thrMenu ? subItem.thrMenu : []; return subItem && subItem.thrMenu ? subItem.thrMenu : [];
},
//
loginState() {
const userId = sessionStorage.getItem('userId');
return userId !== null && userId !== 'null' && userId !== '';
} }
}, },
async mounted() { async mounted() {
@ -134,13 +139,11 @@ export default {
methods: { methods: {
// key // key
getSubItemKey(subItem) { getSubItemKey(subItem) {
// 使ID
return `${subItem.id}_${this.activeCategory}_${subItem.secTitle}`; return `${subItem.id}_${this.activeCategory}_${subItem.secTitle}`;
}, },
// key // key
getThirdItemKey(thirdItem) { getThirdItemKey(thirdItem) {
// 使IDID
return `${thirdItem.id}_${this.activeSubId}_${thirdItem.thrTitle}`; return `${thirdItem.id}_${this.activeSubId}_${thirdItem.thrTitle}`;
}, },
@ -326,9 +329,35 @@ export default {
return descriptions[product.name] || '专业的云服务产品,提供稳定可靠的服务'; 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'); const userId = sessionStorage.getItem('userId');
if (product.type === '百度云') { if (product.type === '百度云') {
@ -354,8 +383,9 @@ export default {
}); });
} }
} else if (product.type === '阿里云') { } else if (product.type === '阿里云') {
if (userId) { if (userId) {
window.open(product.url); await this.handleAliyunProductClick();
} else { } else {
this.$router.push({ this.$router.push({
path: "/login", path: "/login",
@ -397,7 +427,6 @@ export default {
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
/* 样式保持不变 */
.product-service-page { .product-service-page {
margin: 0 auto; margin: 0 auto;
padding: 24px; padding: 24px;

View File

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