This commit is contained in:
ping 2025-11-26 15:43:46 +08:00
commit 3afe8b8f7e
8 changed files with 2139 additions and 1683 deletions

View File

@ -4,7 +4,7 @@
<div id="topContainer" class="container"> <div id="topContainer" class="container">
<!-- 统一显示logo和导航 --> <!-- 统一显示logo和导航 -->
<div class="logo"> <div class="logo">
<!-- Logo图片点击跳转首页 --> <!-- Logo图片点击跳转首页 - 使用与goHome相同的逻辑 -->
<img v-if="JSON.stringify(logoInfoNew) !== '{}'" @click="goHome" style="cursor:pointer;" <img v-if="JSON.stringify(logoInfoNew) !== '{}'" @click="goHome" style="cursor:pointer;"
class="logoImg" :src="logoInfoNew.home.logoImg || ''" alt=""> class="logoImg" :src="logoInfoNew.home.logoImg || ''" alt="">
@ -12,7 +12,7 @@
<nav class="main-nav"> <nav class="main-nav">
<div class="nav-list"> <div class="nav-list">
<!-- 首页 --> <!-- 首页 -->
<p :class="{ active: $route.path.includes('/index') }"> <p :class="{ active: isActiveHome }">
<!-- 动态判断域名跳转首页 --> <!-- 动态判断域名跳转首页 -->
<a @click="goHome">{{ translations[language].home }}</a> <a @click="goHome">{{ translations[language].home }}</a>
</p> </p>
@ -488,6 +488,14 @@ export default Vue.extend({
// ncmatch.cn // ncmatch.cn
isNcmatchDomain() { isNcmatchDomain() {
return window.location.hostname.includes('ncmatch.cn'); return window.location.hostname.includes('ncmatch.cn');
},
//
isActiveHome() {
if (this.isNcmatchDomain) {
return this.$route.path.includes('/ncmatchHome/index');
} else {
return this.$route.path.includes('/homePage/index');
}
} }
}, },
methods: { methods: {
@ -904,7 +912,7 @@ export default Vue.extend({
} }
}, },
// // -
goHome() { goHome() {
if (this.isNcmatchDomain) { if (this.isNcmatchDomain) {
this.$router.push('/ncmatchHome/index'); this.$router.push('/ncmatchHome/index');
@ -949,6 +957,12 @@ export default Vue.extend({
.logoImg { .logoImg {
width: 148px; width: 148px;
cursor: pointer;
transition: opacity 0.3s ease;
&:hover {
opacity: 0.8;
}
} }
} }
@ -972,6 +986,7 @@ export default Vue.extend({
font-size: 18px !important; font-size: 18px !important;
padding: 5px 0; padding: 5px 0;
transition: all 0.3s; transition: all 0.3s;
cursor: pointer;
&:hover { &:hover {
color: #1E6FFF; color: #1E6FFF;
@ -1032,6 +1047,7 @@ export default Vue.extend({
font-size: 14px; font-size: 14px;
text-decoration: none; text-decoration: none;
transition: all 0.3s; transition: all 0.3s;
cursor: pointer;
} }
.login-btn { .login-btn {

View File

@ -1,11 +1,18 @@
<template> <template>
<div class="followBox"> <div class="followBox">
<!-- 页面标题区域 -->
<div class="follow-header">
<div class="followBox-title"> <div class="followBox-title">
<span>我的</span> <span>我的</span>
<span class="hightText">关注</span> <span class="hightText">关注</span>
</div> </div>
<div class="follow-subtitle">管理您关注的企业商品和需求</div>
</div>
<!-- 筛选区域 -->
<div class="filter-section">
<div class="radio-group"> <div class="radio-group">
<label class="radio-item" style="margin-right: 25px;" :class="{ active: publish_type === '1' }"> <label class="radio-item" :class="{ active: publish_type === '1' }">
<input type="radio" v-model="publish_type" value="1" @change="handleTypeChange"> <input type="radio" v-model="publish_type" value="1" @change="handleTypeChange">
<span class="radio-text">企业商品</span> <span class="radio-text">企业商品</span>
</label> </label>
@ -14,13 +21,26 @@
<span class="radio-text">企业需求</span> <span class="radio-text">企业需求</span>
</label> </label>
</div> </div>
<div v-loading="loadingBox" class="history-list" v-if="productList.length > 0">
<div class="history-item" v-for="item in productList" :key="item.id"> <!-- 统计信息 -->
<div class="history-date"> <div class="stats-info" v-if="productList.length > 0">
{{ getTotalItems() }} 个关注
</div>
</div>
<!-- 关注列表 -->
<div v-loading="loadingBox" class="follow-list" v-if="productList.length > 0">
<div class="follow-item" v-for="item in productList" :key="item.id">
<div class="follow-header-info">
<div class="follow-date">
<span class="date-text">{{ formatBrowseDate(item.browse_date) }}</span> <span class="date-text">{{ formatBrowseDate(item.browse_date) }}</span>
<span class="relative-time">{{ getRelativeTime(item.browse_date) }}</span> <span class="relative-time">{{ getRelativeTime(item.browse_date) }}</span>
</div> </div>
<div class="history-content"> <div class="follow-count" v-if="item.products && item.products.length > 0">
关注了 {{ item.products.length }} 个项目
</div>
</div>
<div class="follow-content">
<productCard <productCard
contentType="favorite" contentType="favorite"
:productList="item.products" :productList="item.products"
@ -30,28 +50,38 @@
</div> </div>
</div> </div>
</div> </div>
<!-- 空状态 -->
<div class="empty-state" v-else> <div class="empty-state" v-else>
<div class="empty-icon">📋</div> <div class="empty-content">
<div class="empty-icon"></div>
<div class="empty-text">暂无关注记录</div> <div class="empty-text">暂无关注记录</div>
<div class="empty-desc">您还没有关注任何{{ publish_type === '1' ? '商品' : '需求' }}快去发现精彩内容吧</div>
<div class="empty-action">
<el-button
type="primary"
@click="$router.push('/ncmatchHome')"
class="explore-btn">
去首页探索
</el-button>
</div>
</div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { reqFavoriteSearch } from '@/api/ncmatch'; import { reqFavoriteSearch } from '@/api/ncmatch';
import productCard from '../mainPage/productCard/index.vue'; import productCard from '../mainPage/productCard/index.vue';
import { formatBrowseDate, getRelativeTime } from './tool.js'; import { formatBrowseDate, getRelativeTime } from './tool.js';
export default { export default {
name: 'followBox', name: 'followBox',
data() { data() {
return { return {
loadingBox:false, loadingBox: false,
productList: [], productList: [],
value1: null,
publish_type: '1', // publish_type: '1', //
myProductList: [
]
} }
}, },
created() { created() {
@ -71,22 +101,22 @@ export default {
this.loadingBox = true; this.loadingBox = true;
reqFavoriteSearch({ reqFavoriteSearch({
url_list: window.location.href, url_list: window.location.href,
publish_type: this.publish_type // publish_type: this.publish_type
}).then(res => { }).then(res => {
this.loadingBox = false; this.loadingBox = false;
if (res.status) { if (res.status) {
// product_info
this.productList = this.flattenProductInfo(res.data.favorites); this.productList = this.flattenProductInfo(res.data.favorites);
} else { } else {
this.$message.error(res.msg); this.$message.error(res.msg || '获取关注记录失败');
} }
}) }).catch(error => {
this.loadingBox = false;
this.$message.error('网络错误,请稍后重试');
console.error('获取关注记录失败:', error);
});
}, },
/** /**
* 将product_info的所有子项提升到products中每个产品的上一级去掉product_info字段 * 将product_info的所有子项提升到products中每个产品的上一级去掉product_info字段
* @param {Array} historyList - 原始的历史记录列表
* @returns {Array} 处理后的历史记录列表
*/ */
flattenProductInfo(historyList) { flattenProductInfo(historyList) {
if (!Array.isArray(historyList)) { if (!Array.isArray(historyList)) {
@ -95,26 +125,20 @@ export default {
return historyList.map(historyItem => { return historyList.map(historyItem => {
if (historyItem.products && Array.isArray(historyItem.products)) { if (historyItem.products && Array.isArray(historyItem.products)) {
// products
const flattenedProducts = historyItem.products.map(product => { const flattenedProducts = historyItem.products.map(product => {
if (product.product_info) { if (product.product_info) {
// product_infoproduct
const flattenedProduct = { ...product }; const flattenedProduct = { ...product };
// product_info
Object.keys(product.product_info).forEach(key => { Object.keys(product.product_info).forEach(key => {
if(key == 'id'){ if (key == 'id') {
flattenedProduct.productid = product.product_info.id; flattenedProduct.productid = product.product_info.id;
} }
//
if (!(key in flattenedProduct)) { if (!(key in flattenedProduct)) {
flattenedProduct[key] = product.product_info[key]; flattenedProduct[key] = product.product_info[key];
} }
}); });
// product_info
delete flattenedProduct.product_info; delete flattenedProduct.product_info;
return flattenedProduct; return flattenedProduct;
} }
return product; return product;
@ -130,64 +154,93 @@ export default {
}, },
/** /**
* 处理删除事件从列表中移除被删除的项目 * 处理删除事件从列表中移除被删除的项目
* @param {string} deletedId - 被删除项目的ID
*/ */
handleDeleteItem(deletedId) { handleDeleteItem(deletedId) {
// productList
for (let i = 0; i < this.productList.length; i++) { for (let i = 0; i < this.productList.length; i++) {
const historyItem = this.productList[i]; const historyItem = this.productList[i];
if (historyItem.products && Array.isArray(historyItem.products)) { if (historyItem.products && Array.isArray(historyItem.products)) {
// productsID
const productIndex = historyItem.products.findIndex(product => product.id === deletedId); const productIndex = historyItem.products.findIndex(product => product.id === deletedId);
if (productIndex !== -1) { if (productIndex !== -1) {
//
historyItem.products.splice(productIndex, 1); historyItem.products.splice(productIndex, 1);
//
if (historyItem.products.length === 0) { if (historyItem.products.length === 0) {
this.productList.splice(i, 1); this.productList.splice(i, 1);
} }
//
this.$forceUpdate(); this.$forceUpdate();
this.$message.success('取消关注成功');
break; break;
} }
} }
} }
},
/**
* 获取总关注数
*/
getTotalItems() {
return this.productList.reduce((total, item) => {
return total + (item.products ? item.products.length : 0);
}, 0);
} }
} }
} }
</script> </script>
<style>
<style scoped>
.followBox {
max-width: 1400px;
margin: 0 auto;
padding: 30px 20px;
min-height: calc(100vh - 100px);
}
/* 页面标题区域 */
.follow-header {
text-align: center;
margin-bottom: 40px;
padding: 0 20px;
}
.followBox-title { .followBox-title {
font-size: 32px; font-size: 36px;
font-weight: bold; font-weight: bold;
color: #363a46; color: #363a46;
margin-bottom: 15px; margin-bottom: 12px;
line-height: 1.2;
}
.hightText { .followBox-title .hightText {
background: linear-gradient(90deg, #275aff, #2ebdfa); background: linear-gradient(135deg, #275aff, #2ebdfa);
-webkit-background-clip: text; -webkit-background-clip: text;
background-clip: text; background-clip: text;
color: transparent; color: transparent;
display: inline-block;
font-weight: 700; font-weight: 700;
}
} }
.radio-group-container { .follow-subtitle {
width: 100% !important; font-size: 16px;
color: #666;
opacity: 0.8;
}
/* 筛选区域 */
.filter-section {
display: flex; display: flex;
justify-content: flex-start; justify-content: space-between;
align-items: center; align-items: center;
max-width: 1400px; margin-bottom: 30px;
padding: 0 20px;
flex-wrap: wrap;
gap: 20px;
} }
.radio-group { .radio-group {
display: flex; display: flex;
background: #fff; background: #fff;
border-radius: 8px; border-radius: 12px;
margin-bottom: 15px; padding: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
border: 1px solid #f0f0f0;
} }
.radio-item { .radio-item {
@ -195,110 +248,317 @@ export default {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 8px 16px; padding: 12px 24px;
border-radius: 6px; border-radius: 8px;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.3s ease;
background: #fff; background: transparent;
border: 1px solid #e8e8e8; font-size: 14px;
margin-right: 4px; }
font-size: 16px !important;
&:last-child { .radio-item:last-child {
margin-right: 0; margin-right: 0;
} }
input[type="radio"] { .radio-item input[type="radio"] {
position: absolute; position: absolute;
opacity: 0; opacity: 0;
width: 0; width: 0;
height: 0; height: 0;
}
.radio-text {
font-size: 16px;
color: #275AFF;
font-weight: 500;
transition: all 0.3s ease;
}
&:hover {
border-color: #275AFF;
}
&.active {
background: linear-gradient(to right, #275AFF, #2EBDFA);
border-color: #275AFF;
box-shadow: 0 2px 8px rgba(39, 90, 255, 0.3);
.radio-text {
color: #fff;
}
}
} }
/* 关注记录列表样式 */ .radio-text {
.history-list { font-size: 14px;
color: #666;
font-weight: 500;
transition: all 0.3s ease;
}
.radio-item:hover {
background: rgba(255, 107, 107, 0.05);
}
.radio-item.active {
background: linear-gradient(135deg, #275aff, #2ebdfa);
box-shadow: 0 4px 12px rgba(255, 107, 107, 0.3);
}
.radio-item.active .radio-text {
color: #fff;
font-weight: 600;
}
.stats-info {
font-size: 14px;
color: #666;
background: #f8f9fa;
padding: 8px 16px;
border-radius: 20px;
font-weight: 500;
}
/* 关注列表样式 */
.follow-list {
margin-top: 20px; margin-top: 20px;
} }
.history-item { .follow-item {
background: #fff; background: #fff;
border-radius: 12px; border-radius: 16px;
padding: 20px; padding: 24px;
margin-bottom: 16px; margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
border: 1px solid #f0f0f0; border: 1px solid #f0f0f0;
transition: all 0.3s ease; transition: all 0.3s ease;
position: relative;
overflow: hidden;
} }
.history-item:hover { .follow-item::before {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); content: '';
/* transform: translateY(-2px); */ position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background: linear-gradient(135deg, #275aff, #2ebdfa);
opacity: 0;
transition: opacity 0.3s ease;
} }
.history-date { .follow-item:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
.follow-item:hover::before {
opacity: 1;
}
.follow-header-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 1px solid #f5f5f5;
flex-wrap: wrap;
gap: 15px;
}
.follow-date {
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 16px; gap: 12px;
padding-bottom: 12px; }
border-bottom: 1px solid #f5f5f5;
.date-icon {
font-size: 18px;
} }
.date-text { .date-text {
font-size: 18px; font-size: 18px;
font-weight: 600; font-weight: 600;
color: #275AFF; color: #ff6b6b;
margin-right: 12px;
} }
.relative-time { .relative-time {
font-size: 14px; font-size: 14px;
color: #999; color: #999;
background: #f8f9fa; background: #f8f9fa;
padding: 4px 8px; padding: 6px 12px;
border-radius: 12px; border-radius: 16px;
font-weight: 500;
} }
.history-content { .follow-count {
font-size: 14px;
color: #666;
background: linear-gradient(135deg, #275aff, #2ebdfa);
color: white;
padding: 6px 12px;
border-radius: 16px;
font-weight: 500;
}
.follow-content {
margin-top: 8px; margin-top: 8px;
} }
/* 空状态样式 */ /* 空状态样式 */
.empty-state { .empty-state {
text-align: center; text-align: center;
padding: 60px 20px; padding: 80px 20px;
color: #999; margin-top: 40px;
}
.empty-content {
max-width: 400px;
margin: 0 auto;
} }
.empty-icon { .empty-icon {
font-size: 48px; font-size: 80px;
margin-bottom: 16px; margin-bottom: 24px;
opacity: 0.6; opacity: 0.6;
} }
.empty-text { .empty-text {
font-size: 16px; font-size: 20px;
color: #333;
margin-bottom: 12px;
font-weight: 600;
}
.empty-desc {
font-size: 14px;
color: #999; color: #999;
margin-bottom: 30px;
line-height: 1.6;
}
.explore-btn {
padding: 12px 32px;
border-radius: 8px;
font-weight: 500;
background: linear-gradient(135deg, #275aff, #2ebdfa);
border: none;
transition: all 0.3s ease;
}
.explore-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(255, 107, 107, 0.3);
}
/* 响应式设计 */
@media (max-width: 768px) {
.followBox {
padding: 20px 15px;
}
.follow-header {
margin-bottom: 30px;
}
.followBox-title {
font-size: 28px;
}
.filter-section {
flex-direction: column;
align-items: stretch;
gap: 15px;
}
.radio-group {
width: 100%;
justify-content: center;
}
.radio-item {
flex: 1;
padding: 10px 16px;
}
.stats-info {
align-self: center;
}
.follow-item {
padding: 20px;
border-radius: 12px;
}
.follow-header-info {
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
.follow-date {
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.empty-state {
padding: 60px 15px;
}
.empty-icon {
font-size: 60px;
}
.empty-text {
font-size: 18px;
}
}
@media (max-width: 480px) {
.followBox {
padding: 15px 10px;
}
.followBox-title {
font-size: 24px;
}
.follow-item {
padding: 16px;
margin-bottom: 16px;
}
.date-text {
font-size: 16px;
}
.radio-item {
padding: 8px 12px;
}
.empty-icon {
font-size: 50px;
}
}
/* 加载状态优化 */
::v-deep .el-loading-mask {
border-radius: 16px;
}
::v-deep .el-loading-spinner .circular {
width: 42px;
height: 42px;
}
::v-deep .el-loading-spinner .path {
stroke: #ff6b6b;
}
/* 特殊样式 - 关注页面的独特视觉效果 */
.follow-item .heart-pulse {
position: absolute;
top: 20px;
right: 20px;
width: 24px;
height: 24px;
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ff6b6b'%3E%3Cpath d='M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z'/%3E%3C/svg%3E");
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.1);
opacity: 0.7;
}
100% {
transform: scale(1);
opacity: 1;
}
} }
</style> </style>

View File

@ -1,11 +1,18 @@
<template> <template>
<div class="historyBox"> <div class="historyBox">
<!-- 页面标题 -->
<div class="history-header">
<div class="historyBox-title"> <div class="historyBox-title">
<span>浏览</span> <span>浏览</span>
<span class="hightText">记录</span> <span class="hightText">记录</span>
</div> </div>
<div class="history-subtitle">查看您的商品和需求浏览历史</div>
</div>
<!-- 筛选标签 -->
<div class="filter-section">
<div class="radio-group"> <div class="radio-group">
<label class="radio-item" style="margin-right: 25px;" :class="{ active: publish_type === '1' }"> <label class="radio-item" :class="{ active: publish_type === '1' }">
<input type="radio" v-model="publish_type" value="1" @change="handleTypeChange"> <input type="radio" v-model="publish_type" value="1" @change="handleTypeChange">
<span class="radio-text">企业商品</span> <span class="radio-text">企业商品</span>
</label> </label>
@ -14,12 +21,22 @@
<span class="radio-text">企业需求</span> <span class="radio-text">企业需求</span>
</label> </label>
</div> </div>
<!-- 统计信息 -->
<div class="stats-info" v-if="productList.length > 0">
{{ getTotalItems() }} 条记录
</div>
</div>
<!-- 历史记录列表 -->
<div v-loading="loadingBox" class="history-list" v-if="productList.length > 0"> <div v-loading="loadingBox" class="history-list" v-if="productList.length > 0">
<div class="history-item" v-for="item in productList" :key="item.id"> <div class="history-item" v-for="item in productList" :key="item.id">
<div class="history-date"> <div class="history-date">
<div class="date-content">
<span class="date-text">{{ formatBrowseDate(item.browse_date) }}</span> <span class="date-text">{{ formatBrowseDate(item.browse_date) }}</span>
<span class="relative-time">{{ getRelativeTime(item.browse_date) }}</span> <span class="relative-time">{{ getRelativeTime(item.browse_date) }}</span>
</div> </div>
</div>
<div class="history-content"> <div class="history-content">
<productCard <productCard
contentType="history" contentType="history"
@ -30,28 +47,38 @@
</div> </div>
</div> </div>
</div> </div>
<!-- 空状态 -->
<div class="empty-state" v-else> <div class="empty-state" v-else>
<div class="empty-content">
<div class="empty-icon">📋</div> <div class="empty-icon">📋</div>
<div class="empty-text">暂无浏览记录</div> <div class="empty-text">暂无浏览记录</div>
<div class="empty-desc">您还没有浏览过任何{{ publish_type === '1' ? '商品' : '需求' }}快去探索吧</div>
<div class="empty-action">
<el-button
type="primary"
@click="$router.push('/ncmatchHome')"
class="explore-btn">
去首页逛逛
</el-button>
</div>
</div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { reqUserBrowseHistorySearch } from '@/api/ncmatch'; import { reqUserBrowseHistorySearch } from '@/api/ncmatch';
import productCard from '../mainPage/productCard/index.vue'; import productCard from '../mainPage/productCard/index.vue';
import { formatBrowseDate, getRelativeTime } from './tool.js'; import { formatBrowseDate, getRelativeTime } from './tool.js';
export default { export default {
name: 'historyBox', name: 'historyBox',
data() { data() {
return { return {
loadingBox:false, loadingBox: false,
productList: [], productList: [],
value1: null,
publish_type: '1', // publish_type: '1', //
myProductList: [
]
} }
}, },
created() { created() {
@ -71,22 +98,22 @@ export default {
this.loadingBox = true; this.loadingBox = true;
reqUserBrowseHistorySearch({ reqUserBrowseHistorySearch({
url_list: window.location.href, url_list: window.location.href,
publish_type: this.publish_type // publish_type: this.publish_type
}).then(res => { }).then(res => {
this.loadingBox = false; this.loadingBox = false;
if (res.status) { if (res.status) {
// product_info
this.productList = this.flattenProductInfo(res.data.history_list); this.productList = this.flattenProductInfo(res.data.history_list);
} else { } else {
this.$message.error(res.msg); this.$message.error(res.msg || '获取浏览记录失败');
} }
}) }).catch(error => {
this.loadingBox = false;
this.$message.error('网络错误,请稍后重试');
console.error('获取浏览记录失败:', error);
});
}, },
/** /**
* 将product_info的所有子项提升到products中每个产品的上一级去掉product_info字段 * 将product_info的所有子项提升到products中每个产品的上一级去掉product_info字段
* @param {Array} historyList - 原始的历史记录列表
* @returns {Array} 处理后的历史记录列表
*/ */
flattenProductInfo(historyList) { flattenProductInfo(historyList) {
if (!Array.isArray(historyList)) { if (!Array.isArray(historyList)) {
@ -95,26 +122,20 @@ export default {
return historyList.map(historyItem => { return historyList.map(historyItem => {
if (historyItem.products && Array.isArray(historyItem.products)) { if (historyItem.products && Array.isArray(historyItem.products)) {
// products
const flattenedProducts = historyItem.products.map(product => { const flattenedProducts = historyItem.products.map(product => {
if (product.product_info) { if (product.product_info) {
// product_infoproduct
const flattenedProduct = { ...product }; const flattenedProduct = { ...product };
// product_info
Object.keys(product.product_info).forEach(key => { Object.keys(product.product_info).forEach(key => {
if(key == 'id'){ if (key == 'id') {
flattenedProduct.productid = product.product_info.id; flattenedProduct.productid = product.product_info.id;
} }
//
if (!(key in flattenedProduct)) { if (!(key in flattenedProduct)) {
flattenedProduct[key] = product.product_info[key]; flattenedProduct[key] = product.product_info[key];
} }
}); });
// product_info
delete flattenedProduct.product_info; delete flattenedProduct.product_info;
return flattenedProduct; return flattenedProduct;
} }
return product; return product;
@ -130,64 +151,93 @@ export default {
}, },
/** /**
* 处理删除事件从列表中移除被删除的项目 * 处理删除事件从列表中移除被删除的项目
* @param {string} deletedId - 被删除项目的ID
*/ */
handleDeleteItem(deletedId) { handleDeleteItem(deletedId) {
// productList
for (let i = 0; i < this.productList.length; i++) { for (let i = 0; i < this.productList.length; i++) {
const historyItem = this.productList[i]; const historyItem = this.productList[i];
if (historyItem.products && Array.isArray(historyItem.products)) { if (historyItem.products && Array.isArray(historyItem.products)) {
// productsID
const productIndex = historyItem.products.findIndex(product => product.id === deletedId); const productIndex = historyItem.products.findIndex(product => product.id === deletedId);
if (productIndex !== -1) { if (productIndex !== -1) {
//
historyItem.products.splice(productIndex, 1); historyItem.products.splice(productIndex, 1);
//
if (historyItem.products.length === 0) { if (historyItem.products.length === 0) {
this.productList.splice(i, 1); this.productList.splice(i, 1);
} }
//
this.$forceUpdate(); this.$forceUpdate();
this.$message.success('删除成功');
break; break;
} }
} }
} }
},
/**
* 获取总记录数
*/
getTotalItems() {
return this.productList.reduce((total, item) => {
return total + (item.products ? item.products.length : 0);
}, 0);
} }
} }
} }
</script> </script>
<style>
<style scoped>
.historyBox {
max-width: 1400px;
margin: 0 auto;
padding: 30px 20px;
min-height: calc(100vh - 100px);
}
/* 页面标题区域 */
.history-header {
text-align: center;
margin-bottom: 40px;
padding: 0 20px;
}
.historyBox-title { .historyBox-title {
font-size: 32px; font-size: 36px;
font-weight: bold; font-weight: bold;
color: #363a46; color: #363a46;
margin-bottom: 15px; margin-bottom: 12px;
line-height: 1.2;
}
.hightText { .historyBox-title .hightText {
background: linear-gradient(90deg, #275aff, #2ebdfa); background: linear-gradient(135deg, #275aff, #2ebdfa);
-webkit-background-clip: text; -webkit-background-clip: text;
background-clip: text; background-clip: text;
color: transparent; color: transparent;
display: inline-block;
font-weight: 700; font-weight: 700;
}
} }
.radio-group-container { .history-subtitle {
width: 100% !important; font-size: 16px;
color: #666;
opacity: 0.8;
}
/* 筛选区域 */
.filter-section {
display: flex; display: flex;
justify-content: flex-start; justify-content: space-between;
align-items: center; align-items: center;
max-width: 1400px; margin-bottom: 30px;
padding: 0 20px;
flex-wrap: wrap;
gap: 20px;
} }
.radio-group { .radio-group {
display: flex; display: flex;
background: #fff; background: #fff;
border-radius: 8px; border-radius: 12px;
margin-bottom: 15px; padding: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
border: 1px solid #f0f0f0;
} }
.radio-item { .radio-item {
@ -195,46 +245,53 @@ export default {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 8px 16px; padding: 12px 24px;
border-radius: 6px; border-radius: 8px;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.3s ease;
background: #fff; background: transparent;
border: 1px solid #e8e8e8; font-size: 14px;
margin-right: 4px; }
font-size: 16px !important;
&:last-child { .radio-item:last-child {
margin-right: 0; margin-right: 0;
} }
input[type="radio"] { .radio-item input[type="radio"] {
position: absolute; position: absolute;
opacity: 0; opacity: 0;
width: 0; width: 0;
height: 0; height: 0;
} }
.radio-text { .radio-text {
font-size: 16px; font-size: 14px;
color: #275AFF; color: #666;
font-weight: 500; font-weight: 500;
transition: all 0.3s ease; transition: all 0.3s ease;
} }
&:hover { .radio-item:hover {
border-color: #275AFF; background: rgba(39, 90, 255, 0.05);
} }
&.active { .radio-item.active {
background: linear-gradient(to right, #275AFF, #2EBDFA); background: linear-gradient(135deg, #275AFF, #2EBDFA);
border-color: #275AFF; box-shadow: 0 4px 12px rgba(39, 90, 255, 0.3);
box-shadow: 0 2px 8px rgba(39, 90, 255, 0.3); }
.radio-text { .radio-item.active .radio-text {
color: #fff; color: #fff;
} font-weight: 600;
} }
.stats-info {
font-size: 14px;
color: #666;
background: #f8f9fa;
padding: 8px 16px;
border-radius: 20px;
font-weight: 500;
} }
/* 历史记录列表样式 */ /* 历史记录列表样式 */
@ -244,40 +301,66 @@ export default {
.history-item { .history-item {
background: #fff; background: #fff;
border-radius: 12px; border-radius: 16px;
padding: 20px; padding: 24px;
margin-bottom: 16px; margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
border: 1px solid #f0f0f0; border: 1px solid #f0f0f0;
transition: all 0.3s ease; transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.history-item::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background: linear-gradient(to bottom, #275AFF, #2EBDFA);
opacity: 0;
transition: opacity 0.3s ease;
} }
.history-item:hover { .history-item:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
/* transform: translateY(-2px); */ transform: translateY(-2px);
}
.history-item:hover::before {
opacity: 1;
} }
.history-date { .history-date {
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 1px solid #f5f5f5;
}
.date-content {
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 16px; gap: 12px;
padding-bottom: 12px; }
border-bottom: 1px solid #f5f5f5;
.date-icon {
font-size: 18px;
} }
.date-text { .date-text {
font-size: 18px; font-size: 18px;
font-weight: 600; font-weight: 600;
color: #275AFF; color: #275AFF;
margin-right: 12px;
} }
.relative-time { .relative-time {
font-size: 14px; font-size: 14px;
color: #999; color: #999;
background: #f8f9fa; background: #f8f9fa;
padding: 4px 8px; padding: 6px 12px;
border-radius: 12px; border-radius: 16px;
font-weight: 500;
} }
.history-content { .history-content {
@ -287,18 +370,145 @@ export default {
/* 空状态样式 */ /* 空状态样式 */
.empty-state { .empty-state {
text-align: center; text-align: center;
padding: 60px 20px; padding: 80px 20px;
color: #999; margin-top: 40px;
}
.empty-content {
max-width: 400px;
margin: 0 auto;
} }
.empty-icon { .empty-icon {
font-size: 48px; font-size: 80px;
margin-bottom: 16px; margin-bottom: 24px;
opacity: 0.6; opacity: 0.6;
} }
.empty-text { .empty-text {
font-size: 16px; font-size: 20px;
color: #333;
margin-bottom: 12px;
font-weight: 600;
}
.empty-desc {
font-size: 14px;
color: #999; color: #999;
margin-bottom: 30px;
line-height: 1.6;
}
.explore-btn {
padding: 12px 32px;
border-radius: 8px;
font-weight: 500;
background: linear-gradient(135deg, #275AFF, #2EBDFA);
border: none;
transition: all 0.3s ease;
}
.explore-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(39, 90, 255, 0.3);
}
/* 响应式设计 */
@media (max-width: 768px) {
.historyBox {
padding: 20px 15px;
}
.history-header {
margin-bottom: 30px;
}
.historyBox-title {
font-size: 28px;
}
.filter-section {
flex-direction: column;
align-items: stretch;
gap: 15px;
}
.radio-group {
width: 100%;
justify-content: center;
}
.radio-item {
flex: 1;
padding: 10px 16px;
}
.stats-info {
align-self: center;
}
.history-item {
padding: 20px;
border-radius: 12px;
}
.date-content {
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.empty-state {
padding: 60px 15px;
}
.empty-icon {
font-size: 60px;
}
.empty-text {
font-size: 18px;
}
}
@media (max-width: 480px) {
.historyBox {
padding: 15px 10px;
}
.historyBox-title {
font-size: 24px;
}
.history-item {
padding: 16px;
margin-bottom: 16px;
}
.date-text {
font-size: 16px;
}
.radio-item {
padding: 8px 12px;
}
.empty-icon {
font-size: 50px;
}
}
/* 加载状态优化 */
::v-deep .el-loading-mask {
border-radius: 16px;
}
::v-deep .el-loading-spinner .circular {
width: 42px;
height: 42px;
}
::v-deep .el-loading-spinner .path {
stroke: #275AFF;
} }
</style> </style>

View File

@ -1,24 +1,11 @@
<template> <template>
<div id="homeOut" class="homeOut"> <div id="homeOut" class="homeOut">
<TopBox id="topBox"></TopBox> <TopBox id="topBox"></TopBox>
<div class="banner-box">
<div class="conter">
<div class="title">
供需广场
</div>
<div class="banner-title">
开元云供需对接广场是企业级云服务合作核心阵地围绕云资源算力网络服务模型及应用企业可灵活发布资源供给或业务需求通过精准匹配打破信息壁垒这里既能盘活企业闲置算力与云资源又能快速对接所需技术服务与合作机会助力企业降本增效加速数字化与智能化转型
</div>
<div class="btn">
<div class="btn-box" @click="sendInfo('2')">发布需求</div>
<div class="btn-box" @click="sendInfo('1')">发布商品</div>
</div>
</div>
</div>
<!-- <div class="search-box"> <!-- <div class="search-box">
<search></search> <search></search>
</div> --> </div> -->
<div style="width: 90%;max-width: 1600px;"> <div style="width: 100%;">
<router-view></router-view> <router-view></router-view>
</div> </div>
@ -92,7 +79,6 @@ import { mapGetters, mapState } from "vuex";
import search from "./search/index.vue"; import search from "./search/index.vue";
import { reqEnterpriseAuditInfoSearch } from '@/api/ncmatch' import { reqEnterpriseAuditInfoSearch } from '@/api/ncmatch'
import productDetail from "./proDetail/index.vue"; import productDetail from "./proDetail/index.vue";
export default Vue.extend({ export default Vue.extend({
name: "indexLast", name: "indexLast",
components: { components: {
@ -228,54 +214,7 @@ export default Vue.extend({
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
/deep/.banner-box {
height: 700px!important;
background: url('./img/tt-banner.png') no-repeat ;
width: 100%;
padding-bottom: 200px;
.conter {
width: 700px;
position: relative;
display: flex;
flex-direction: column;
align-items: flex-start;
margin: 140px 240px 0px;
}
.title {
font-size: 30px;
font-weight: 600;
margin-bottom: 18px;
}
.banner-title {
width: 700px;
font-size: 16px;
color: #8890ab;
line-height: 2 !important;
}
.btn{
display: flex;
}
.btn-box{
padding: 10px 20px;
background: linear-gradient(90deg, rgb(39, 90, 255), rgb(46, 189, 250));
border-radius: 14px;
color: #fff;
margin-right: 20px;
margin-top: 20px;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(39, 90, 255, 0.3);
}
}
}
.search-box { .search-box {
width: 100%; width: 100%;

View File

@ -1,7 +1,97 @@
<template>
<div class="jd-homepage">
<!-- 搜索区域 -->
<div class="search-box">
<search></search>
</div>
<!-- 主内容区域 -->
<main class="main-content">
<div class="content-wrapper">
<!-- 左侧菜单 -->
<menuAside></menuAside>
<!-- 中间主内容 -->
<section class="main-section">
<!-- 顶部促销横幅 -->
<div id="banner" class="banner">
<div class="centerBox">
<div class="title">
<span class="leftText">NCMatch</span>
<span class="rightText">算力供需对接平台</span>
</div>
<ul class="tagUl">
<li>匠造臻品 <span class="separator"></span> 精奢惠选</li>
</ul>
</div>
</div>
</section>
<!-- 右侧用户信息栏 -->
<aside class="user-sidebar">
<div class="sidebar-content">
<span class="publish-goods" @click="$router.push('/ncmatchHome/supplyAndDemandSquare')">
算力供需广场
</span>
<span class="publish-goods" @click="sendInfo('2')">发布需求</span>
<span class="publish-goods" @click="sendInfo('1')">发布商品</span>
<ul class="userBtn">
<li @click="goHistory">
<img src="./img/eye.png" alt="浏览记录">
浏览记录
</li>
<li @click="goFavorite">
<img src="./img/collect.png" alt="我的收藏">
我的收藏
</li>
</ul>
</div>
</aside>
</div>
</main>
<!-- 产品列表区域 -->
<div class="productList">
<ul class="myTab">
<li v-for="menu in product" :class="currentHotMenu === menu.id ? 'activeMenu' : ''" @click="clickNetMenu(menu)"
:key="menu.name">
<span class="tab-text">{{ menu.product_category }}</span>
</li>
</ul>
<productCard v-loading="boxLoading" :productList="hotProductList"></productCard>
<el-pagination @current-change="handleCurrentChange" :page-size="page_size" layout="total, prev, pager, next"
:total="total">
</el-pagination>
</div>
<!-- 发布商品/需求对话框 -->
<el-dialog :title="publish_type === '2' ? '发布需求' : '发布商品'" width="60vw" top="5vh"
:visible.sync="sendProductVisible">
<sendProduct :isEdit="false" v-if="publish_type" @success="sendProductSuccess" :publish_type="publish_type">
</sendProduct>
</el-dialog>
<!-- 提示对话框 -->
<el-dialog title="温馨提示" :visible.sync="showTip" width="30%">
<span>您还没有完善企业信息完善企业信息审核通过后您可以发布需求与商品</span>
<span slot="footer" class="dialog-footer">
<span>
<span style="margin-right: 10px;">跳转到</span>
<el-button size="small" type="primary" @click="goInfo">信息完善</el-button>
</span>
</span>
</el-dialog>
</div>
</template>
<script> <script>
import Vue from 'vue' import Vue from 'vue'
import { reqPublishProductSearchFirstPage, reqEnterpriseAuditInfoSearch, reqHomepageProductCategory, reqGetSupplyAndDemandSquareList } from '@/api/ncmatch' import { reqPublishProductSearchFirstPage, reqEnterpriseAuditInfoSearch, reqHomepageProductCategory, reqGetSupplyAndDemandSquareList } from '@/api/ncmatch'
import { mapGetters, mapState } from "vuex"; import { mapGetters, mapState } from "vuex";
export default Vue.extend({ export default Vue.extend({
name: "mainPage", name: "mainPage",
components: { components: {
@ -12,7 +102,6 @@ export default Vue.extend({
}, },
created() { created() {
this.getHomepageProductCategory() this.getHomepageProductCategory()
}, },
data() { data() {
return { return {
@ -24,110 +113,12 @@ export default Vue.extend({
sendProductVisible: false, sendProductVisible: false,
currentHotMenu: "", currentHotMenu: "",
product: [], product: [],
hotProductList: [ hotProductList: [],
// {
// id: 1,
// name: 'NVIDIA-4090',
// image: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE1MCIgdmlld0JveD0iMCAwIDIwMCAxNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTUwIiBmaWxsPSIjMjIyMjIyIi8+Cjx0ZXh0IHg9IjEwMCIgeT0iNzUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxNCIgZmlsbD0id2hpdGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiPk5WSUQpQSBSVFggNDA5MDwvdGV4dD4KPC9zdmc+Cg==',
// price: 6000,
// cpu: 'AMD EPYC 7542 32 C * 2',
// memory: '64G DDR4-3200 * 8',
// gpu: 'NVIDIA-4090-24GB * 8',
// sys_disk: '960G SATA SSD * 2 (Raid)',
// data_disk: '3.84T U.2 NVMe SSD * 1',
// net_card: 'Mellanox Connect4 25G SFP28 2-port * 1'
// },
// {
// id: 2,
// name: 'NVIDIA-4090',
// image: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE1MCIgdmlld0JveD0iMCAwIDIwMCAxNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTUwIiBmaWxsPSIjMjIyMjIyIi8+Cjx0ZXh0IHg9IjEwMCIgeT0iNzUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxNCIgZmlsbD0id2hpdGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiPk5WSUQpQSBSVFggNDA5MDwvdGV4dD4KPC9zdmc+Cg==',
// price: 6000,
// cpu: 'AMD EPYC 7542 32 C * 2',
// memory: '64G DDR4-3200 * 8',
// gpu: 'NVIDIA-4090-24GB * 8',
// sys_disk: '960G SATA SSD * 2 (Raid)',
// data_disk: '3.84T U.2 NVMe SSD * 1',
// net_card: 'Mellanox Connect4 25G SFP28 2-port * 1'
// }
],
hotMenuList: [
{
id: "hot",
name: "热门推荐",
icon: require("../../newImg/niu.png"),
activeIcon: require("../../newImg/niuActive.png")
},
{
id: "hlzx",
name: "互联网专线",
icon: require("../../newImg/niu.png"),
activeIcon: require("../../newImg/niuActive.png"),
},
{
id: "SDWAN",
name: "SDWAN",
icon: require("../../newImg/niu.png"),
activeIcon: require("../../newImg/niuActive.png"),
},
{
id: "DCI",
name: "DCI",
icon: require("../../newImg/niu.png"),
activeIcon: require("../../newImg/niuActive.png"),
},
{
id: "AI",
name: "AI专线",
icon: require("../../newImg/niu.png"),
activeIcon: require("../../newImg/niuActive.png"),
}
],
searchKeyword: '',
hotSearchKeywords: [
'昆仑芯-P800',
'天垓-150',
'4090',
'云服务器-GPU',
'人脸识别',
'SDWAN',
'互联网专线',
'DCI',
'AI专线',
'对象存储',
'智慧医疗',
'智慧客服',
'A800',
'A100',
'910B'
],
topNavItems: [
{ name: '早十好价', icon: '10', color: 'red' },
{ name: '司法拍卖', icon: '⚖️', color: 'red' },
{ name: '企业购', icon: '🏢', color: 'blue' },
{ name: '京东新品', icon: '🎁', color: 'blue' },
{ name: '男装馆', icon: '👔', color: 'blue' },
{ name: '黑色星期五', icon: '🍃', color: 'green' },
{ name: '服饰美妆', icon: '👗', color: 'purple' },
{ name: '清洁馆', icon: '💧', color: 'blue' },
{ name: '五金城', icon: '⚙️', color: 'red' },
{ name: '全部频道', icon: '📱', color: 'rainbow' }
],
categories: [
{ name: '云', icon: require('./img/cloud.png'), product_list: ['百度云'] },
{ name: '国产算力', icon: require('./img/computing.png'), product_list: ['昇腾910B', 'P800', '其他'] },
{ name: 'NVIDIA', icon: require('./img/nvidia.png'), product_list: ['3090', '4090', '5080', '5090'] },
{ name: '网', icon: require('./img/net.png'), product_list: ['AI专线', 'SDWAN', '互联网专线', 'DCI'] },
{ name: '一体机', icon: require('./img/computer.png'), product_list: ['昆仑芯', '天数智芯'] },
{ name: '硬件', icon: require('./img/ying.png'), product_list: ['机器人', 'AR眼镜'] },
{ name: 'AI应用', icon: require('./img/aiApp.png'), product_list: ['数字人', '智慧医疗', '智能客服'] },
],
current_page: 1, current_page: 1,
page_size: 8, page_size: 8,
} }
}, },
computed: { computed: {
...mapGetters(["sidebar", "avatar", "device"]), ...mapGetters(["sidebar", "avatar", "device"]),
...mapState({ ...mapState({
isShowPanel: (state) => state.product.showHomeNav, isShowPanel: (state) => state.product.showHomeNav,
@ -161,12 +152,10 @@ export default Vue.extend({
goInfo() { goInfo() {
this.showTip = false this.showTip = false
this.$router.push('/customer/approve') this.$router.push('/customer/approve')
}, },
handleCurrentChange(val) { handleCurrentChange(val) {
this.current_page = val this.current_page = val
this.initData() this.initData()
// this.init_product_list()
}, },
sendInfo(type) { sendInfo(type) {
if (this.loginState) { if (this.loginState) {
@ -200,8 +189,6 @@ export default Vue.extend({
this.showTip = true this.showTip = true
} }
}) })
} else { } else {
this.$router.push('/login') this.$router.push('/login')
} }
@ -226,13 +213,18 @@ export default Vue.extend({
this.hotProductList = res.data[0].product_list this.hotProductList = res.data[0].product_list
this.total = res.data[0].total_count this.total = res.data[0].total_count
} }
} }
}) })
}, },
init_product_list() { init_product_list() {
reqPublishProductSearchFirstPage({ publish_type: "1", url_link: window.location.href, to_page: 'first_page', page_size: this.page_size, current_page: this.current_page, product_type: this.currentHotMenu }).then(res => { reqPublishProductSearchFirstPage({
publish_type: "1",
url_link: window.location.href,
to_page: 'first_page',
page_size: this.page_size,
current_page: this.current_page,
product_type: this.currentHotMenu
}).then(res => {
if (res.status) { if (res.status) {
if (res.data.length > 0) { if (res.data.length > 0) {
this.currentHotMenu = res.data[0].id this.currentHotMenu = res.data[0].id
@ -240,7 +232,6 @@ export default Vue.extend({
this.product = res.data this.product = res.data
this.total = res.data[0].total_count this.total = res.data[0].total_count
} }
} }
}) })
}, },
@ -253,11 +244,6 @@ export default Vue.extend({
this.currentHotMenu = menu.id; this.currentHotMenu = menu.id;
this.selectedCategory = menu.id this.selectedCategory = menu.id
this.initData() this.initData()
// this.hotProductList = menu.product_list || [];
},
handleSearch() {
console.log('搜索:1', this.searchKeyword)
}, },
goFavorite() { goFavorite() {
if (this.loginState) { if (this.loginState) {
@ -277,155 +263,55 @@ export default Vue.extend({
}) })
</script> </script>
<template>
<div class="jd-homepage">
<!-- 顶部Header -->
<!-- <header class="header">
<div class="header-content">
</div>
</header> -->
<!-- 主内容区域 -->
<main class="main-content">
<div class="content-wrapper">
<menuAside></menuAside>
<!-- 中间主内容 -->
<section class="main-section">
<!-- 顶部促销横幅 -->
<div id="banner" class="banner">
<div class="centerBox">
<span style="margin-top: 100px" class="title">
<span class="leftText">
NCMatch &nbsp;&nbsp;
</span>
<span class="rightText" style="margin-left: 10px;"> 算力供需对接平台</span>
</span>
<!-- <span class="description">支持模型训练推理和数据处理灵活配置助您高效释放AI潜能</span>-->
<ul style="margin-top: 50px" class="tagUl">
<li>匠造臻品 <span style="display: inline-block;margin: 0 15px;"></span> 精奢惠选</li>
</ul>
</div>
</div>
</section>
<!-- 右侧用户信息栏 -->
<aside class="user-sidebar" style="background-color: #f8fbfe;padding: 10px;padding-top: 20px;">
<span class="publish-goods" @click="$router.push('/ncmatchHome/supplyAndDemandSquare')"> 算力供需广场</span>
<span class="publish-goods" @click="sendInfo('2')">发布需求</span>
<span class="publish-goods" @click="sendInfo('1')">发布商品</span>
<ul class="userBtn">
<li @click="goHistory"><img style="width: 26px;height: 26px;" src="./img/eye.png" alt="">浏览记录</li>
<li @click="goFavorite"><img style="width: 26px;height: 26px;" src="./img/collect.png" alt="">我的收藏</li>
<!-- <li><img src="./img/like.png" alt="">关注需求</li> -->
</ul>
</aside>
</div>
</main>
<div class="productList">
<ul class="myTab">
<li v-for="menu in product" :class="currentHotMenu === menu.id ? 'activeMenu' : ''" @click="clickNetMenu(menu)"
:key="menu.name">
<!-- <img :src="currentHotMenu === menu.id ? menu.activeIcon : menu.icon" alt="" /> -->
<span class="tab-text">{{ menu.product_category }}</span>
</li>
</ul>
<productCard v-loading="boxLoading" :productList="hotProductList"></productCard>
<el-pagination style="background-color: white;" @current-change="handleCurrentChange" :page-size="page_size"
layout="total, prev, pager, next" :total="total">
</el-pagination>
<el-dialog :title="publish_type === '2' ? '发布需求' : '发布商品'" width="60vw" top="5vh"
:visible.sync="sendProductVisible">
<sendProduct :isEdit="false" v-if="publish_type" @success="sendProductSuccess" :publish_type="publish_type">
</sendProduct>
</el-dialog>
<el-dialog title="温馨提示" :visible.sync="showTip" width="30%">
<span>您还没有完善企业信息完善企业信息审核通过后您可以发布需求与商品</span>
<span slot="footer" class="dialog-footer">
<span> <span style="margin-right: 10px;"> 跳转到</span> <el-button size="small" type="primary"
@click="goInfo">信息完善</el-button></span>
</span>
</el-dialog>
</div>
</div>
</template>
<style scoped lang="scss"> <style scoped lang="scss">
.userBtn { .jd-homepage {
margin-top: 40px !important; margin: 30px;
display: flex; margin-top: 100px;
justify-content: space-around; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
align-items: center;
li {
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
* {
img {
width: 20px !important;
height: 20px !important;
}
}
&:hover {
transition: all 0.3s;
cursor: pointer;
color: #2c96fc;
svg {
path {
fill: #2c96fc !important;
}
}
}
img {
width: 16px;
height: 16px;
}
}
} }
.search-box {
margin-bottom: 20px;
.productList {
width: 100%;
max-width: 1600px;
margin: 0 auto;
// padding-bottom: 45px;
} }
.publish-goods { /* 主内容区域 */
margin-bottom: 25px; .main-content {
display: flex; padding: 40px;
justify-content: center;
align-items: center;
width: 100%; width: 100%;
height: 60px; margin-top: 50px;
border-radius: 10px; border-radius: 10px;
background: url('./img//btnBg.png');
background-size: cover;
background-position: top;
color: #222F60 !important;
font-size: 18px;
font-weight: 900;
&:hover { .content-wrapper {
cursor: pointer; box-sizing: border-box;
// background: #2EBDFA; display: grid;
grid-template-columns: 300px 1fr 300px;
gap: 20px;
@media (max-width: 1200px) {
grid-template-columns: 250px 1fr 250px;
gap: 15px;
}
@media (max-width: 992px) {
grid-template-columns: 200px 1fr;
.user-sidebar {
grid-column: 1 / -1;
margin-top: 20px;
}
}
@media (max-width: 768px) {
grid-template-columns: 1fr;
.user-sidebar {
margin-top: 20px;
}
}
} }
} }
/* 横幅样式 */
.banner { .banner {
border-radius: 10px; border-radius: 10px;
background: url("../../newImg/banner.png"); background: url("../../newImg/banner.png");
@ -433,7 +319,11 @@ export default Vue.extend({
background-size: cover; background-size: cover;
background-position: center; background-position: center;
position: relative; position: relative;
height: 100%; height: 500px;
@media (max-width: 768px) {
height: 200px;
}
.centerBox { .centerBox {
display: flex; display: flex;
@ -441,7 +331,7 @@ export default Vue.extend({
align-items: center; align-items: center;
flex-direction: column; flex-direction: column;
position: absolute; position: absolute;
top: 35%; top: 50%;
left: 50%; left: 50%;
width: 100%; width: 100%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
@ -450,27 +340,35 @@ export default Vue.extend({
color: #222F60; color: #222F60;
font-size: 42px; font-size: 42px;
display: flex; display: flex;
flex-wrap: wrap;
justify-content: center;
text-align: center;
@media (max-width: 992px) {
font-size: 36px;
}
@media (max-width: 768px) {
font-size: 28px;
flex-direction: column;
}
.leftText { .leftText {
background: linear-gradient(to right, #275AFF, #2EBDFA); background: linear-gradient(to right, #275AFF, #2EBDFA);
/* 渐变方向颜色 */
-webkit-background-clip: text; -webkit-background-clip: text;
/* 关键属性:裁剪背景到文字 */
background-clip: text; background-clip: text;
color: transparent; color: transparent;
/* 文字颜色透明 */
display: inline-block; display: inline-block;
/* 确保渐变生效 */
}
} }
.rightText {
margin-left: 10px;
.description { @media (max-width: 768px) {
margin: 15px 0; margin-left: 0;
color: #7A82A0; margin-top: 5px;
font-size: 24px; }
height: 75px; }
line-height: 1.6;
} }
.tagUl { .tagUl {
@ -479,19 +377,109 @@ export default Vue.extend({
justify-content: center; justify-content: center;
align-items: center; align-items: center;
font-size: 24px; font-size: 24px;
margin-top: 30px;
@media (max-width: 768px) {
font-size: 18px;
margin-top: 20px;
}
li { li {
padding: 0 30px; padding: 0 30px;
margin-top: 25px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
}
&:not(:last-child) { .separator {
border-right: 2px solid #D2D7E6; display: inline-block;
margin: 0 15px;
} }
} }
} }
}
/* 用户侧边栏 */
.user-sidebar {
background-color: #f8fbfe;
border-radius: 10px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
.sidebar-content {
display: flex;
flex-direction: column;
height: 100%;
}
.publish-goods {
margin-bottom: 15px;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 60px;
border-radius: 10px;
background: url('./img/btnBg.png');
background-size: cover;
background-position: top;
color: #222F60 !important;
font-size: 18px;
font-weight: 900;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
@media (max-width: 992px) {
height: 50px;
font-size: 16px;
}
}
.userBtn {
margin-top: 40px;
display: flex;
flex-direction: column;
gap: 15px;
li {
font-size: 14px;
display: flex;
align-items: center;
padding: 12px 15px;
border-radius: 8px;
transition: all 0.3s;
cursor: pointer;
background-color: white;
&:hover {
background-color: #e6f7ff;
color: #2c96fc;
transform: translateX(5px);
}
img {
width: 20px;
height: 20px;
margin-right: 10px;
}
}
}
}
/* 产品列表区域 */
.productList {
width: 100%;
max-width: 1600px;
margin: 0 auto;
padding: 0 20px 40px;
@media (max-width: 768px) {
padding: 0 10px 30px;
} }
} }
@ -505,6 +493,13 @@ export default Vue.extend({
justify-content: center; justify-content: center;
align-items: center; align-items: center;
margin-top: 50px; margin-top: 50px;
flex-wrap: wrap;
@media (max-width: 768px) {
margin-top: 30px;
font-size: 16px;
}
.activeMenu { .activeMenu {
background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%); background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%);
color: white; color: white;
@ -513,243 +508,69 @@ export default Vue.extend({
li { li {
transition: all .2s ease-in-out; transition: all .2s ease-in-out;
z-index: 2; z-index: 2;
padding: 10px 20px; padding: 10px 100px;
margin-right: 45px; margin: 5px;
border-radius: 8px; border-radius: 8px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
&:hover {
cursor: pointer !important;
}
img {
width: 24px;
height: 24px;
margin-right: 5px;
}
}
}
.jd-homepage {
min-height: 100vh;
background-color: #f8fbfe;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
// Header
.header {
padding: 10px 0;
display: flex;
justify-content: flex-start;
align-items: center;
position: relative;
.header-content {
width: 100%;
max-width: 950px;
// margin: 0 auto;
margin-top: 15px;
display: flex;
align-items: center;
// gap: 20px;
position: absolute;
left: 420px;
}
.logo-section {
margin: 0 50px;
img {
height: 70px;
width: 70px;
}
.logo {
display: flex;
align-items: center;
height: 100%;
.logo-text {
font-size: 24px;
font-weight: bold;
color: #e1251b;
}
.logo-mascot {
font-size: 20px;
}
}
}
.signin-section {
.signin-btn {
background: linear-gradient(45deg, #ffd700, #ff6b35);
color: white;
border: none;
padding: 8px 15px;
border-radius: 4px;
cursor: pointer; cursor: pointer;
font-size: 12px;
@media (max-width: 768px) {
padding: 8px 15px;
margin: 3px;
}
}
}
/* 分页样式 */
.el-pagination {
padding: 20px 0;
display: flex; display: flex;
align-items: center; justify-content: center;
gap: 5px;
.signin-icon { @media (max-width: 768px) {
font-weight: bold;
}
}
}
}
::v-deep .el-pagination__total,
// ::v-deep .el-pagination__jump {
.main-content {
// padding: 16px;
width: 100%;
max-width: 1600px;
margin: 20px auto;
margin-top: 10px;
margin-bottom: 0;
border-radius: 10px;
overflow: visible;
background-color: white;
height: 320px;
.content-wrapper {
box-sizing: border-box;
display: grid;
grid-template-columns: 300px 1fr 250px;
gap: 20px;
}
}
//
.main-section {
.promo-banner {
background: linear-gradient(45deg, #52c41a, #73d13d);
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
position: relative;
height: 100%;
.banner-content {
display: flex;
justify-content: space-between;
align-items: center;
.banner-text {
color: white;
h3 {
margin: 0 0 10px 0;
font-size: 24px;
}
p {
margin: 5px 0;
font-size: 16px;
}
}
.banner-image {
position: relative;
.fridge-img {
width: 120px;
height: 90px;
object-fit: cover;
}
.flower-decoration {
position: absolute;
bottom: 0;
right: 0;
font-size: 20px;
}
}
}
}
.subsidy-section,
.live-section,
.seckill-section {
background: white;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
h3 {
margin: 0;
font-size: 18px;
color: #333;
}
.official-tag,
.live-tag,
.seckill-tag {
background: #e1251b;
color: white;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
}
}
}
}
.user-sidebar {
height: 100%;
* {
font-size: 20px !important;
}
}
//
@media (max-width: 1200px) {
.main-content .content-wrapper {
grid-template-columns: 180px 1fr 220px;
gap: 15px;
}
}
@media (max-width: 768px) {
.main-content .content-wrapper {
grid-template-columns: 1fr;
}
.category-sidebar,
.user-sidebar {
display: none; display: none;
} }
}
}
.header .header-content { /* 响应式设计 */
flex-direction: column; @media (max-width: 576px) {
gap: 10px; .jd-homepage {
margin: 15px;
margin-top: 80px;
} }
.search-section { .main-content {
width: 100%; padding: 20px;
margin-top: 30px;
}
.banner {
height: 150px;
.centerBox .title {
font-size: 22px;
}
.centerBox .tagUl {
font-size: 16px;
margin-top: 15px;
}
}
.user-sidebar .publish-goods {
height: 45px;
font-size: 14px;
}
.myTab {
font-size: 14px;
margin-top: 20px;
} }
} }
</style> </style>

View File

@ -2,60 +2,69 @@
<!-- 左侧分类导航 --> <!-- 左侧分类导航 -->
<aside class="category-sidebar"> <aside class="category-sidebar">
<ul class="category-list"> <ul class="category-list">
<li class="category-item" style="color: #E02020;"><img src="../img/hot.svg" style="margin-right: 10px;" <li class="category-item special-item">
alt=""> 热门推荐 / 活动促销</li> <img src="../img/hot.svg" alt="热门推荐">
热门推荐 / 活动促销
</li>
<li v-for="category in categories" :key="category.id" class="category-item" <li v-for="category in categories" :key="category.id" class="category-item"
@mouseenter="showProductList(category)" @mouseleave="hideProductList"> @mouseenter="showProductList(category)" @mouseleave="hideProductList"
<span class="category-icon"> <img style="width: 24px;height: 24px;" :src="category.icon" alt=""> </span> :class="{ 'category-item-active': currentCategory && currentCategory.id === category.id }">
<span class="category-icon">
<img :src="category.icon" :alt="category.first_level_name">
</span>
<span class="category-name">{{ category.first_level_name }}</span> <span class="category-name">{{ category.first_level_name }}</span>
<span style="display: flex;margin-left: 0px;padding-left: 0px;">|</span> <span class="category-divider">|</span>
<div class="menu-item"> <div class="menu-item">
<span v-for="(secondary, index) in category.secondaryClassification" :key="secondary.id"> <span v-for="(secondary, index) in category.secondaryClassification" :key="secondary.id">
{{ secondary.second_level_name }}{{ index < category.secondaryClassification.length - 1 ? ' / ' {{ secondary.second_level_name }}{{ index < category.secondaryClassification.length - 1 ? ' / ' : '' }}
: '' }} </span> </span>
</div> </div>
<span class="category-arrow"></span>
</li> </li>
</ul> </ul>
<transition name="slide-fade"> <transition name="slide-fade">
<!-- v-if="currentCategory" -->
<div v-loading="loading" element-loading-text="加载中..." element-loading-spinner="el-icon-loading" <div v-loading="loading" element-loading-text="加载中..." element-loading-spinner="el-icon-loading"
element-loading-background="rgba(255, 255, 255, 0.8)" class="rightBox" v-if="currentCategory" element-loading-background="rgba(255, 255, 255, 0.8)" class="rightBox" v-if="currentCategory"
@mouseenter="keepProductList" @mouseleave="hideProductList"> @mouseenter="keepProductList" @mouseleave="hideProductList">
<div class="rightBox-content"> <div class="rightBox-content">
<!-- 二级菜单标题 --> <!-- 二级菜单标题 -->
<div class="secondary-menu"> <div class="secondary-menu">
<div v-for="secondary in currentCategory.secondaryClassification" :key="secondary.id" <div v-for="secondary in currentCategory.secondaryClassification" :key="secondary.id" class="secondary-item"
class="secondary-item" :class="{ active: selectedSecondary === secondary }" :class="{
@mouseenter="selectSecondary(secondary)"> active: selectedSecondary === secondary,
'has-children': secondary.thirdClassification && secondary.thirdClassification.length > 0
}"
@mouseenter="selectSecondary(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>
</div> </div>
</div> </div>
<!-- 三级和四级菜单内容 --> <!-- 三级和四级菜单内容 -->
<div class="menu-content"> <div class="menu-content">
<!-- 如果有选中的二级菜单且有三级菜单显示京东风格的分类区域 --> <!-- 如果有选中的二级菜单且有三级菜单显示京东风格的分类区域 -->
<div v-if="selectedSecondary && selectedSecondary.thirdClassification && selectedSecondary.thirdClassification.length > 0" <div
v-if="selectedSecondary && selectedSecondary.thirdClassification && selectedSecondary.thirdClassification.length > 0"
class="jd-style-menu"> class="jd-style-menu">
<div v-for="third in selectedSecondary.thirdClassification" :key="third.id" <div v-for="third in selectedSecondary.thirdClassification" :key="third.id" class="category-section">
class="category-section">
<!-- 只有当有四级菜单时才显示三级菜单标题 --> <!-- 只有当有四级菜单时才显示三级菜单标题 -->
<div v-if="third.product_list && third.product_list.length > 0" class="section-header"> <div v-if="third.product_list && third.product_list.length > 0" class="section-header">
<span class="section-title">{{ third.third_level_name }}</span> <span class="section-title">{{ third.third_level_name }}</span>
<span class="section-arrow">></span> <span class="section-arrow"></span>
</div> </div>
<div class="section-content"> <div class="section-content">
<!-- 如果有四级菜单product_list直接显示所有四级菜单项 --> <!-- 如果有四级菜单product_list直接显示所有四级菜单项 -->
<div v-if="third.product_list && third.product_list.length > 0" <div v-if="third.product_list && third.product_list.length > 0" class="product-grid">
class="product-grid"> <div @click="goSearch(product)" v-for="(product, index) in third.product_list" :key="product.id"
<div @click="goSearch(product)" v-for="(product, index) in third.product_list"
:key="product.id"
class="product-tag"> class="product-tag">
{{ product.first_level_name }} {{ product.first_level_name }}
</div> </div>
</div> </div>
<!-- 如果没有四级菜单将三级菜单项视为四级菜单项显示 --> <!-- 如果没有四级菜单将三级菜单项视为四级菜单项显示 -->
<div v-else class="product-grid"> <div v-else class="product-grid">
<div @click="openTalk" class="product-tag "> <div @click="openTalk" class="product-tag special-tag">
{{ third.third_level_name }} {{ third.third_level_name }}
</div> </div>
</div> </div>
@ -68,11 +77,11 @@
<div class="category-section"> <div class="category-section">
<div class="section-header"> <div class="section-header">
<span class="section-title">{{ selectedSecondary.second_level_name }}</span> <span class="section-title">{{ selectedSecondary.second_level_name }}</span>
<span class="section-arrow">></span> <span class="section-arrow"></span>
</div> </div>
<div class="section-content"> <div class="section-content">
<div class="product-grid"> <div class="product-grid">
<div class="product-tag hot-tag"> <div class="product-tag special-tag" @click="goSearch(selectedSecondary)">
{{ selectedSecondary.second_level_name }} {{ selectedSecondary.second_level_name }}
</div> </div>
</div> </div>
@ -86,11 +95,11 @@
class="category-section"> class="category-section">
<div class="section-header"> <div class="section-header">
<span class="section-title">{{ secondary.second_level_name }}</span> <span class="section-title">{{ secondary.second_level_name }}</span>
<span class="section-arrow">></span> <span class="section-arrow"></span>
</div> </div>
<div class="section-content"> <div class="section-content">
<div class="product-grid"> <div class="product-grid">
<div class="product-tag "> <div class="product-tag" @click="goSearch(secondary)">
{{ secondary.second_level_name }} {{ secondary.second_level_name }}
</div> </div>
</div> </div>
@ -104,11 +113,13 @@
<Talk></Talk> <Talk></Talk>
</aside> </aside>
</template> </template>
<script> <script>
import { reqNcMatchMenu, reqSearch } from '@/api/ncmatch'; import { reqNcMatchMenu, reqSearch } from '@/api/ncmatch';
import { buildDynamicStructure } from './buildNcmatchTree'; import { buildDynamicStructure } from './buildNcmatchTree';
import Talk from '@/views/homePage/dialog/talk/index.vue'; import Talk from '@/views/homePage/dialog/talk/index.vue';
import eventBus from '@/utils/eventBus' import eventBus from '@/utils/eventBus'
export default { export default {
name: 'menuAside', name: 'menuAside',
components: { components: {
@ -123,6 +134,7 @@ export default {
loading: false, loading: false,
keyword: '', keyword: '',
publish_type: '', publish_type: '',
activeSection: null
} }
}, },
created() { created() {
@ -169,9 +181,16 @@ export default {
}); });
}, },
goSearch(product) { goSearch(product) {
console.log("product",product); console.log("product", product);
if(product.source=='search'){
//
const element = event.target;
element.classList.add('click-animation');
setTimeout(() => {
element.classList.remove('click-animation');
}, 300);
if (product.source == 'search') {
// search /ncmatchHome/search // search /ncmatchHome/search
const keywordFromItem = product && (product.first_level_name || product.product_name || product.second_level_name) const keywordFromItem = product && (product.first_level_name || product.product_name || product.second_level_name)
this.keyword = keywordFromItem || this.keyword || '' this.keyword = keywordFromItem || this.keyword || ''
@ -192,11 +211,9 @@ export default {
} }
}) })
this.performFormalSearch(this.keyword, this.publish_type); this.performFormalSearch(this.keyword, this.publish_type);
}else{ } else {
this.openTalk() this.openTalk()
} }
}, },
async openTalk() { async openTalk() {
@ -210,14 +227,6 @@ export default {
if (res.status) { if (res.status) {
this.categories = buildDynamicStructure(res.data) this.categories = buildDynamicStructure(res.data)
console.log("测试", this.categories); console.log("测试", this.categories);
// this.categories = res.data.map(item => {
// return {
// name: item.name,
// icon: item.icon,
// product_list: item.product_list
// }
// });
} }
}) })
}, },
@ -239,6 +248,18 @@ export default {
this.selectedSecondary = secondary; this.selectedSecondary = secondary;
}, },
handleSecondaryClick(secondary) {
this.selectSecondary(secondary);
//
if (event) {
const element = event.target;
element.classList.add('click-feedback');
setTimeout(() => {
element.classList.remove('click-feedback');
}, 300);
}
},
keepProductList() { keepProductList() {
// //
if (this.hideTimer) { if (this.hideTimer) {
@ -263,84 +284,111 @@ export default {
} }
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.menu-item { .category-sidebar {
position: relative;
background-color: #f8fbfe;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
height: 100%;
border-radius: 10px;
padding: 15px 5px;
width: 100%;
box-sizing: border-box;
}
.category-list {
list-style: none;
padding: 0;
margin: 0;
width: 100%;
.category-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
cursor: pointer;
transition: all 0.3s;
border-radius: 6px;
margin-bottom: 8px;
position: relative;
&:hover {
color: #2c96fc !important;
background: #e6f3ff !important;
transform: translateX(5px);
box-shadow: 0 2px 8px rgba(44, 150, 252, 0.2);
}
&.category-item-active {
color: #2c96fc !important;
background: #e6f3ff !important;
border-right: 3px solid #2c96fc;
}
&.special-item {
color: #E02020;
font-weight: 600;
img {
margin-right: 8px;
width: 18px;
height: 18px;
}
}
.category-icon {
width: 24px;
height: 24px;
display: flex;
justify-content: center;
align-items: center;
img {
width: 100%;
height: 100%;
object-fit: contain;
transition: transform 0.3s ease;
}
}
&:hover .category-icon img {
transform: scale(1.1);
}
.category-name {
font-size: 15px;
font-weight: 500;
white-space: nowrap;
}
.category-divider {
color: #e0e0e0;
margin: 0 4px;
}
.menu-item {
flex: 1; flex: 1;
font-size: 14px; font-size: 14px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
min-width: 0; min-width: 0;
/* 确保flex子项可以收缩 */ color: #666;
}
//
.category-sidebar {
position: relative;
background-color: #f8fbfe;
height: 100%;
border-radius: 10px;
// padding: 15px;
padding: 5px;
.category-list {
list-style: none;
padding: 0;
margin: 0;
.category-item {
display: flex;
margin-bottom: 10px;
align-items: center;
gap: 10px;
padding: 4px 0;
cursor: pointer;
transition: all 0.3s;
padding: 0 10px;
&:hover {
color: #2c96fc !important;
background: #c3daee !important;
} }
&:last-child { .category-arrow {
border-bottom: none; color: #ccc;
}
.category-icon {
width: 20px;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
}
.category-name {
// font-size: 14px;
font-size: 16px; font-size: 16px;
} font-weight: bold;
} transition: transform 0.3s ease, color 0.3s ease;
}
}
@media (max-width: 768px) {
.main-content .content-wrapper {
grid-template-columns: 1fr;
} }
.category-sidebar, &:hover .category-arrow {
.user-sidebar { color: #2c96fc;
display: none; transform: translateX(3px);
} }
.header .header-content {
flex-direction: column;
gap: 10px;
}
.search-section {
width: 100%;
} }
} }
@ -348,18 +396,18 @@ export default {
position: absolute; position: absolute;
left: 100%; left: 100%;
top: 0; top: 0;
width: 1000px; width: 900px;
background: white; background: white;
border-radius: 8px; border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
z-index: 9999; z-index: 1000;
padding: 25px; padding: 20px;
min-height: 350px; min-height: 400px;
margin-left: 10px; margin-left: 8px;
overflow: visible; overflow: visible;
white-space: nowrap; border: 1px solid #e8e8e8;
animation: fadeInScale 0.3s ease;
/* 添加一个透明的连接区域,防止鼠标移动时意外消失 */
&::before { &::before {
content: ''; content: '';
position: absolute; position: absolute;
@ -371,40 +419,92 @@ export default {
} }
.rightBox-content { .rightBox-content {
height: 100%;
.secondary-menu { .secondary-menu {
line-height: 1.5;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 15px; gap: 12px;
margin-bottom: 20px; margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #f0f0f0;
.secondary-item { .secondary-item {
padding: 4px 8px; padding: 8px 16px;
background: #f8f9fa; background: #f5f7fa;
border-radius: 4px; border-radius: 6px;
cursor: pointer; cursor: pointer;
display: flex;
transition: all 0.3s ease; transition: all 0.3s ease;
font-size: 16px; font-size: 14px;
color: #333; color: #333;
font-weight: 500; font-weight: 500;
display: flex;
align-items: center;
gap: 5px;
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
transition: left 0.5s;
}
&:hover::before {
left: 100%;
}
&:hover, &:hover,
&.active { &.active {
background-color: #ffebf1; background: linear-gradient(to right, #275AFF, #2EBDFA);
color: #ff0f23; color: #fff;
box-shadow: 0 4px 12px rgba(39, 90, 255, 0.3);
transform: translateY(-2px);
}
&.has-children::after {
content: '';
position: absolute;
bottom: -2px;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 2px;
background: #275AFF;
transition: width 0.3s ease;
}
&.active.has-children::after {
width: 80%;
}
&.click-feedback {
animation: subtle-pulse 0.3s ease;
}
.item-arrow {
font-size: 16px;
font-weight: bold;
transition: transform 0.3s ease;
}
&:hover .item-arrow,
&.active .item-arrow {
transform: translateX(3px);
} }
} }
} }
.menu-content { .menu-content {
min-height: 200px; min-height: 280px;
height: 100%; max-height: 500px;
max-height: calc(100vh - 280px);
overflow-y: auto; overflow-y: auto;
padding-right: 5px;
.jd-style-menu { .jd-style-menu {
width: 100%; width: 100%;
@ -414,19 +514,20 @@ export default {
.category-section { .category-section {
display: flex; display: flex;
justify-content: flex-start; align-items: flex-start;
align-items: center; margin-bottom: 15px;
.section-header { .section-header {
display: flex; display: flex;
align-items: center; align-items: center;
min-width: 120px;
margin-right: 20px;
.section-title { .section-title {
font-size: 16px; font-size: 15px;
font-weight: 600; font-weight: 600;
color: #333; color: #333;
margin-right: 8px; margin-right: 6px;
} }
.section-arrow { .section-arrow {
@ -436,78 +537,66 @@ export default {
} }
.section-content { .section-content {
line-height: 1.5; flex: 1;
.product-grid { .product-grid {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 10px; gap: 12px;
align-items: center;
.product-tag { .product-tag {
height: fit-content !important; padding: 8px 16px;
font-size: 14px; font-size: 14px;
color: #666; color: #666;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease; transition: all 0.3s ease;
white-space: nowrap; border-radius: 20px;
background: #f8f9fa;
position: relative; position: relative;
overflow: hidden;
border: 1px solid transparent;
&:hover { &::before {
color: #e1251b;
}
}
}
}
}
}
.hot-tag {
color: #e1251b !important;
border-color: #e1251b !important;
background: #fff5f5 !important;
font-weight: 500 !important;
position: relative;
&::after {
content: ''; content: '';
position: absolute; position: absolute;
bottom: -1px; top: 0;
left: 0; left: -100%;
width: 100%; width: 100%;
height: 2px; height: 100%;
background: #e1251b; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.5), transparent);
border-radius: 0 0 4px 4px; transition: left 0.5s;
}
} }
.product-list { &:hover::before {
flex: 1; left: 100%;
display: grid; }
grid-template-columns: repeat(3, 1fr);
gap: 12px;
align-content: start;
justify-content: center;
.product-item {
width: fit-content;
border-radius: 6px;
text-align: center;
transition: all 0.3s ease;
cursor: pointer;
&:hover { &:hover {
* { color: #275AFF;
color: #2c96fc; background: #f0f7ff;
border-color: #275AFF;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(39, 90, 255, 0.2);
} }
&.click-animation {
animation: subtle-bounce 0.3s ease;
}
} }
.product-name { .special-tag {
font-size: 14px; color: #275AFF !important;
color: #333; border: 1px solid #275AFF !important;
font-weight: 500; background: #f0f7ff !important;
font-weight: 500 !important;
&:hover {
color: #275AFF !important;
background: #e0efff !important;
box-shadow: 0 4px 8px rgba(39, 90, 255, 0.2) !important;
}
}
}
} }
} }
} }
@ -515,8 +604,40 @@ export default {
} }
} }
.category-sidebar { /* 动画定义 */
position: relative; @keyframes fadeInScale {
0% {
opacity: 0;
transform: scale(0.95) translateX(10px);
}
100% {
opacity: 1;
transform: scale(1) translateX(0);
}
}
@keyframes subtle-pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.02);
}
100% {
transform: scale(1);
}
}
@keyframes subtle-bounce {
0%, 20%, 60%, 100% {
transform: translateY(0);
}
40% {
transform: translateY(-3px);
}
80% {
transform: translateY(-1px);
}
} }
/* 过渡动画样式 */ /* 过渡动画样式 */
@ -525,22 +646,41 @@ export default {
} }
.slide-fade-leave-active { .slide-fade-leave-active {
transition: all 0.3s ease; transition: all 0.2s ease;
} }
.slide-fade-enter { .slide-fade-enter {
// transform: translateX(-20px);
opacity: 0; opacity: 0;
transform: translateX(10px);
} }
.slide-fade-leave-to { .slide-fade-leave-to {
// transform: translateX(-20px);
opacity: 0; opacity: 0;
transform: translateX(10px);
} }
.slide-fade-enter-to, .slide-fade-enter-to,
.slide-fade-leave { .slide-fade-leave {
// transform: translateX(0);
opacity: 1; opacity: 1;
transform: translateX(0);
}
/* 响应式设计 */
@media (max-width: 1200px) {
.rightBox {
width: 800px;
}
}
@media (max-width: 992px) {
.rightBox {
width: 700px;
}
}
@media (max-width: 768px) {
.category-sidebar {
display: none;
}
} }
</style> </style>

View File

Before

Width:  |  Height:  |  Size: 873 KiB

After

Width:  |  Height:  |  Size: 873 KiB

View File

@ -1,4 +1,19 @@
<template> <template>
<div class="conter">
<div class="banner-box">
<div class="conter">
<div class="Toptitle">
供需广场
</div>
<div class="banner-title">
开元云供需对接广场是企业级云服务合作核心阵地围绕云资源算力网络服务模型及应用企业可灵活发布资源供给或业务需求通过精准匹配打破信息壁垒这里既能盘活企业闲置算力与云资源又能快速对接所需技术服务与合作机会助力企业降本增效加速数字化与智能化转型
</div>
<div class="btn">
<div class="btn-box" @click="sendInfo('2')">发布需求</div>
<div class="btn-box" @click="sendInfo('1')">发布商品</div>
</div>
</div>
</div>
<div class="main-box"> <div class="main-box">
<!-- <div style="margin-top: 25px;"> <!-- <div style="margin-top: 25px;">
<span style="margin-top: 100px;" class="title"> <span style="margin-top: 100px;" class="title">
@ -87,11 +102,10 @@
<!-- 产品列表 --> <!-- 产品列表 -->
<div class="product-card-container"> <div class="product-card-container">
<div v-if="productList.length > 0"> <div v-if="productList.length > 0">
<productCard type="supplyAndDemandSquare" :publish_type="publish_type" <productCard type="supplyAndDemandSquare" :publish_type="publish_type" :productList="productList">
:productList="productList">
</productCard> </productCard>
<el-pagination @current-change="handleCurrentChange" :page-size="page_size" <el-pagination @current-change="handleCurrentChange" :page-size="page_size" layout="total, prev, pager, next"
layout="total, prev, pager, next" :total="total" style="width: 100%; text-align: center;"> :total="total" style="width: 100%; text-align: center;">
</el-pagination> </el-pagination>
</div> </div>
<div v-else class="no-data"> <div v-else class="no-data">
@ -100,6 +114,7 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</template> </template>
<script> <script>
@ -249,7 +264,7 @@ export default {
}, },
init_product_category() { init_product_category() {
return reqSupplyAndDemandFirstCategory({to_page: 'show', url_link: window.location.href }).then(res => { return reqSupplyAndDemandFirstCategory({ to_page: 'show', url_link: window.location.href }).then(res => {
if (res.status) { if (res.status) {
this.firstCategories = Array.isArray(res.data) ? res.data : [] this.firstCategories = Array.isArray(res.data) ? res.data : []
if (this.firstCategories.length > 0) { if (this.firstCategories.length > 0) {
@ -269,7 +284,7 @@ export default {
}, },
loadSecondCategories(firstId) { loadSecondCategories(firstId) {
return reqSupplyAndDemandFirstCategory({ to_page: 'show',url_link: window.location.href, first_level_id: firstId }).then(res => { return reqSupplyAndDemandFirstCategory({ to_page: 'show', url_link: window.location.href, first_level_id: firstId }).then(res => {
if (res.status) { if (res.status) {
this.secondCategories = Array.isArray(res.data) ? res.data : [] this.secondCategories = Array.isArray(res.data) ? res.data : []
} else { } else {
@ -320,6 +335,60 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.conter{
display: flex;
flex-direction: column;
align-items: center;
}
.banner-box {
height: 700px!important;
background: url('./img/tt-banner.png') no-repeat ;
width: 100%;
padding-bottom: 200px;
.conter {
width: 700px;
position: relative;
display: flex;
flex-direction: column;
align-items: flex-start;
margin: 100px 280px 0px;
}
.Toptitle {
font-size: 30px;
font-weight: 600;
margin-bottom: 18px;
color: #000;
}
.banner-title {
width: 700px;
font-size: 16px;
color: #8890ab;
line-height: 2 !important;
}
.btn{
display: flex;
}
.btn-box{
padding: 10px 20px;
background: linear-gradient(90deg, rgb(39, 90, 255), rgb(46, 189, 250));
border-radius: 14px;
color: #fff;
margin-right: 20px;
margin-top: 20px;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(39, 90, 255, 0.3);
}
}
}
.title { .title {
color: #666 !important; color: #666 !important;
font-size: 36px; font-size: 36px;
@ -340,7 +409,8 @@ export default {
} }
.main-box { .main-box {
width: 100%; width: 90%;
// min-width: 1600px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;