2025-12-04 16:26:16 +08:00

592 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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 style="margin-top: 25px;">
<span style="margin-top: 100px;" class="title">
<span class="leftText">
算力供需
</span>
<span class="rightText"> 广场</span></span>
</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 class="filter-card">
<!-- <div class="filter-header">
<span class="filter-title">筛选条件</span>
</div> -->
<div class="filter-content">
<!-- 发布类型 -->
<div class="filter-section">
<div class="filter-label">发布类型</div>
<div class="filter-options">
<label class="filter-option" :class="{ active: publish_type === '1' }">
<input type="radio" v-model="publish_type" value="1" @change="handleTypeChange">
<span class="option-text">企业商品</span>
</label>
<label class="filter-option" :class="{ active: publish_type === '2' }">
<input type="radio" v-model="publish_type" value="2" @change="handleTypeChange">
<span class="option-text">企业需求</span>
</label>
</div>
</div>
<!-- 所属类别 -->
<div class="filter-section">
<div class="filter-label">所属类别</div>
<div class="filter-options">
<span v-for="category in firstCategories" :key="category.id"
:class="['filter-option', { active: activeFirstId === category.id }]"
@click="selectFirstCategory(category.id)">
{{ category.product_category || category.label }}
</span>
</div>
</div>
<!-- 所属品类 -->
<div class="filter-section" v-if="secondCategories.length > 0">
<div class="filter-label">所属品类</div>
<div class="filter-options">
<span v-for="category in secondCategories" :key="category.id"
:class="['filter-option', { active: activeSecondId === category.id }]"
@click="selectSecondCategory(category.id)">
{{ category.product_category || category.label }}
</span>
</div>
</div>
<!-- 公司类别 -->
<!-- <div class="filter-section">
<div class="filter-label">公司类别</div>
<div class="filter-options">
<span v-for="company in companies" :key="company.id"
:class="['filter-option', { active: selectedCompanies.includes(company.value) }]"
@click="selectCompany(company.value)">
{{ company.label }}
</span>
</div>
</div> -->
</div>
</div>
<!-- 产品列表 -->
<div class="product-card-container">
<div v-if="productList.length > 0">
<productCard type="supplyAndDemandSquare" :publish_type="publish_type" :productList="productList">
</productCard>
<el-pagination @current-change="handleCurrentChange" :page-size="page_size" layout="total, prev, pager, next"
:total="total" style="width: 100%; text-align: center;">
</el-pagination>
</div>
<div v-else class="no-data">
<img style="width: 150px;height: 150px;" src="./img/empty.svg" alt="">
暂无数据
</div>
</div>
</div>
</div>
</template>
<script>
import { reqGetSupplyAndDemandSquareList, reqCompanyCategorySearch, reqSupplyAndDemandFirstCategory, reqEnterpriseAuditInfoSearch } from '@/api/ncmatch'
import { mapGetters, mapState } from 'vuex'
export default {
name: 'supplyAndDemandSquare',
components: {
productCard: () => import('@/views/homePage/ncmatch/mainPage/productCard/index.vue'),
sendProduct: () => import('@/views/homePage/ncmatch/mainPage/sendProduct/index.vue')
},
data() {
return {
page_size: 8,
current_page: 1,
typeList: [],
publish_type: '1',
selectedCategory: '',
selectedCompanies: [],
categories: [],
companies: [],
productList: [],
total: 0,
firstCategories: [],
secondCategories: [],
activeFirstId: null,
activeSecondId: null,
showTip: false,
sendProductVisible: false,
}
},
created() {
this.initAllData()
},
computed: {
isNcmatchHome() {
return window.location.href.includes('ncmatchHome')
},
...mapGetters(["sidebar", "avatar", "device"]),
...mapState({
isShowPanel: (state) => state.product.showHomeNav,
navIndex: (state) => state.product.navIndex,
gridObj: state => state.operationAnalysis.gridObj,
mybalance: state => state.user.mybalance,
logoutUrl: state => state.login.logoutUrl,
loginStateVuex: state => state.login.loginState,
logoInfoNew: state => state.product.logoInfoNew,
}),
loginState() {
const userId = sessionStorage.getItem('userId');
return this.loginStateVuex || (userId !== null && userId !== 'null' && userId !== '');
},
},
methods: {
// 发布需求/商品方法
sendInfo(type) {
if (this.loginState) {
reqEnterpriseAuditInfoSearch({
url_link: window.location.href,
}).then(res => {
const dataList = res && res.data && res.data.data
const hasAuditInfo = Array.isArray(dataList) && dataList.length !== 0
const roles = sessionStorage.getItem('jueseNew')
const isCustomer = roles ? roles.includes('客户') : true
if (hasAuditInfo && dataList[0]) {
const auditStatus = dataList[0].audit_status
if (auditStatus === 'pending') {
this.$message.warning('您的审核状态为待审核,请等待审核通过后发布~')
} else if (auditStatus === 'rejected') {
this.$message.warning('您的审核状态为驳回,请重新提交~')
} else {
this.publish_type = type
this.sendProductVisible = true
}
} else if (!isCustomer) {
this.publish_type = type
this.sendProductVisible = true
} else {
this.showTip = true
}
})
} else {
this.$router.push('/login')
}
},
// 发布成功回调
sendProductSuccess() {
this.sendProductVisible = false;
this.initData()
},
// 跳转到信息完善页面
goInfo() {
this.showTip = false
this.$router.push('/customer/approve')
},
handleCurrentChange(val) {
this.current_page = val
this.initData()
},
initAllData() {
this.init_product_category().then(() => {
this.init_company_category().then(() => {
this.initData()
})
})
},
init_company_category() {
return reqCompanyCategorySearch({ url_link: window.location.href }).then(res => {
if (res.status) {
this.company_category_list = []
for (let item of res.data) {
this.company_category_list.push({
label: item.company_category,
value: item.company_category
})
}
this.companies = this.company_category_list
}
return res
})
},
// 选中一级分类
selectFirstCategory(id) {
if (id === this.activeFirstId) return
this.activeFirstId = id
this.activeSecondId = null
this.selectedCategory = id
this.loadSecondCategories(id)
this.initData()
},
// 选中二级分类
selectSecondCategory(id) {
if (id === this.activeSecondId) return
this.activeSecondId = id
this.selectedCategory = id
this.initData()
},
init_product_category() {
return reqSupplyAndDemandFirstCategory({ to_page: 'show', url_link: window.location.href }).then(res => {
if (res.status) {
this.firstCategories = Array.isArray(res.data) ? res.data : []
if (this.firstCategories.length > 0) {
this.activeFirstId = this.firstCategories[0].id
this.selectedCategory = this.activeFirstId
this.activeSecondId = null
return this.loadSecondCategories(this.activeFirstId)
} else {
this.activeFirstId = null
this.activeSecondId = null
this.selectedCategory = ''
this.secondCategories = []
}
}
return res
})
},
loadSecondCategories(firstId) {
return reqSupplyAndDemandFirstCategory({ to_page: 'show', url_link: window.location.href, first_level_id: firstId }).then(res => {
if (res.status) {
this.secondCategories = Array.isArray(res.data) ? res.data : []
} else {
this.secondCategories = []
}
return res
})
},
handleTypeChange() {
console.log('切换到:', this.publish_type)
this.initData()
},
initData() {
let ploay = {
product_category: this.selectedCategory,
to_page: "square",
url_link: window.location.href,
page_size: this.page_size,
current_page: this.current_page,
company_type: this.selectedCompanies.length > 0 ? this.selectedCompanies.join(",") : "",
publish_type: this.publish_type
}
reqGetSupplyAndDemandSquareList(ploay).then(res => {
if (res.status) {
if (res.data.length === 0) {
this.productList = []
} else {
this.productList = res.data[0].product_list
}
this.total = res.data[0].total_count
}
})
},
selectCompany(companyId) {
const index = this.selectedCompanies.indexOf(companyId)
if (index > -1) {
this.selectedCompanies.splice(index, 1)
} else {
this.selectedCompanies.push(companyId)
}
this.initData()
},
}
}
</script>
<style scoped lang="scss">
.conter{
display: flex;
flex-direction: column;
align-items: center;
}
.banner-box {
height: 600px!important;
background: url('./img/tt-banner.png') no-repeat ;
width: 100%;
// padding-bottom: 10px;
.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 {
color: #666 !important;
font-size: 36px;
margin: 25px;
.leftText {
background: linear-gradient(to right, #275AFF, #2EBDFA);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
display: inline-block;
}
}
.product-card-container {
margin-top: 30px;
width: 100%;
}
.main-box {
width: 90%;
// min-width: 1600px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
max-width: 1600px;
}
/* 筛选卡片样式 */
.filter-card {
width: 100%;
max-width: 1400px;
background: white;
border-radius: 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
margin: 20px 0;
transition: all 0.3s ease;
overflow: hidden;
&:hover {
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.12);
}
}
.filter-header {
padding: 20px 25px;
border-bottom: 1px solid #f0f0f0;
background: linear-gradient(135deg, #f8faff 0%, #f0f4ff 100%);
}
.filter-title {
font-size: 18px;
font-weight: 600;
color: #111e52;
display: flex;
align-items: center;
&::before {
content: '';
display: inline-block;
width: 4px;
height: 16px;
background: linear-gradient(135deg, #275AFF, #2EBDFA);
border-radius: 2px;
margin-right: 10px;
}
}
.filter-content {
padding: 25px;
}
.filter-section {
display: flex;
align-items: flex-start;
margin-bottom: 25px;
&:last-child {
margin-bottom: 0;
}
}
.filter-label {
font-size: 14px;
color: #333;
font-weight: 500;
white-space: nowrap;
flex-shrink: 0;
line-height: 32px;
width: 80px;
margin-right: 20px;
}
.filter-options {
display: flex;
flex-wrap: wrap;
gap: 12px;
flex: 1;
}
.filter-option {
position: relative;
padding: 8px 20px;
border: 1px solid #e0e0e0;
border-radius: 8px;
font-size: 14px;
color: #666;
cursor: pointer;
transition: all 0.3s ease;
background: white;
display: flex;
align-items: center;
justify-content: center;
min-width: 80px;
text-align: center;
&:hover {
border-color: #275AFF;
color: #275AFF;
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(39, 90, 255, 0.2);
}
&.active {
background: linear-gradient(135deg, #275AFF, #2EBDFA);
color: white;
border-color: #275AFF;
box-shadow: 0 2px 8px rgba(39, 90, 255, 0.3);
}
/* 隐藏原生radio */
input[type="radio"] {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.option-text {
font-weight: 500;
pointer-events: none;
}
}
.no-data {
min-height: 500px;
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
color: #7A82A0;
flex-direction: column;
}
.el-pagination {
text-align: center;
margin-top: 30px;
}
/* 响应式调整 */
@media (max-width: 768px) {
.filter-card {
margin: 15px 10px;
}
.filter-content {
padding: 15px;
}
.filter-section {
flex-direction: column;
margin-bottom: 20px;
}
.filter-label {
width: 100%;
margin-bottom: 10px;
margin-right: 0;
}
.filter-options {
width: 100%;
gap: 8px;
}
.filter-option {
padding: 6px 12px;
font-size: 12px;
min-width: 60px;
}
}
@media (max-width: 1200px) {
.filter-card {
max-width: calc(100% - 40px);
margin: 20px;
}
}
</style>