This commit is contained in:
木瓜一块八 2025-07-31 13:29:53 +08:00
parent e303b98fd1
commit 69d7fb2540
15 changed files with 2482 additions and 517 deletions

View File

@ -51,6 +51,7 @@
"uuid": "^9.0.0", "uuid": "^9.0.0",
"vue": "2.6.10", "vue": "2.6.10",
"vue-count-to": "^1.0.13", "vue-count-to": "^1.0.13",
"vue-cropper": "^0.6.5",
"vue-device-detector": "^1.1.6", "vue-device-detector": "^1.1.6",
"vue-infinite-scroll": "^2.0.2", "vue-infinite-scroll": "^2.0.2",
"vue-router": "^3.0.2", "vue-router": "^3.0.2",

View File

@ -0,0 +1,40 @@
import request from "@/utils/request";
//获取商品分类
export function reqGetProductCategorySearch(data) {
return request({
url: '/product/product_category_search.dspy',
method: 'post',
headers: {
'Content-Type': 'application/json'
},
data
})
}
//添加产品 publish_product_add
export function reqPublishProductAdd(data) {
return request({
url: '/product/publish_product_add.dspy',
method: 'post',
headers: { 'Content-Type': 'multipart/form-data' },
data
})
}
//获公司类型 /product/company_category_search.dspy
export function reqCompanyCategorySearch(data) {
return request({
url: '/product/company_category_search.dspy',
method: 'post',
headers: { 'Content-Type': 'application/json' },
data
})
}
//获取全部产品/product/publish_product_search_first_page.dspy
export function reqPublishProductSearchFirstPage(data) {
return request({
url: '/product/publish_product_search_first_page.dspy',
method: 'post',
headers: { 'Content-Type': 'application/json' },
data
})
}

View File

@ -175,6 +175,13 @@ export const constantRoutes = [
hidden: true, hidden: true,
meta: {title: "首页", fullPath: "/ncmatch/mainPage/index"}, meta: {title: "首页", fullPath: "/ncmatch/mainPage/index"},
}, },
{
path: "supplyAndDemandSquare",
component: () => import("@/views/homePage/ncmatch/supplyAndDemandSquare/index.vue"),
name: "supplyAndDemandSquare",
hidden: true,
meta: {title: "算力供需广场", fullPath: "/ncmatch/supplyAndDemandSquare"},
},
] ]
}, },
{ {

View File

@ -4,14 +4,13 @@
<div class="centerBox"> <div class="centerBox">
<span style="margin-top: 100px" class="title"> <span style="margin-top: 100px" class="title">
<span v-if=" JSON.stringify(logoInfoNew)!=='{}'" class="leftText"> <span v-if="JSON.stringify(logoInfoNew) !== '{}'" class="leftText">
{{ logoInfoNew.home.bannerTitle || "" }} {{ logoInfoNew.home.bannerTitle || "" }}
</span> </span>
<span <span class="rightText"> 您身边的AI管家</span></span>
class="rightText"> 您身边的AI管家</span></span>
<!-- <span class="description">支持模型训练推理和数据处理灵活配置助您高效释放AI潜能</span>--> <!-- <span class="description">支持模型训练推理和数据处理灵活配置助您高效释放AI潜能</span>-->
<ul style="margin-top: 50px" class="tagUl"> <ul style="margin-top: 50px" class="tagUl">
<li> AI </li> <li> AI </li>
@ -20,33 +19,31 @@
</ul> </ul>
</div> </div>
<div style="cursor: pointer" class="tag"> <div style="cursor: pointer" class="tag">
<span @click="openTalk" class="product"> <span @click="openTalk" class="product">
<span class="productName">4090 裸金属</span> <span class="productName">4090 裸金属</span>
<span class="price"><span class="smallText"></span> <span class="bigText">6000</span> <span <span class="price"><span class="smallText"></span> <span class="bigText">6000</span> <span
class="smallText">//</span> </span> class="smallText">//</span> </span>
</span> </span>
<span @click="goBaiduFn" style="top: 180px" class="product"> <span @click="goBaiduFn" style="top: 180px" class="product">
<span class="productName">云资源</span> <span class="productName">云资源</span>
<span class="price"> <span class="price">
<span <span style="margin-top: 15px" class="smallText">直降 <span style="font-size: 30px;display: inline-block"
style="margin-top: 15px" class="bigText">20%</span></span> </span>
class="smallText">直降 <span style="font-size: 30px;display: inline-block" </span>
class="bigText">20%</span></span> </span>
</span>
</div> </div>
</div> </div>
<div class="base"> <div class="base">
<div class="bg"></div> <div class="bg"></div>
<div class="itemTitle"> <div class="itemTitle">
<span class="topText"> <span>云筑</span><span class="commonTextColor">企业基座</span></span> <span class="topText"> <span>云筑</span><span class="commonTextColor">企业基座</span></span>
<span class="bottomText">多云融合 让上云更简单</span> <span class="bottomText">多云融合 让上云更简单</span>
</div> </div>
<ul class="myTab"> <ul class="myTab">
<li v-for="menu in baseMenu" :class="currentBaseMenu ===menu.id?'activeMenu':''" @click="clickBaseMenu(menu)" <li v-for="menu in baseMenu" :class="currentBaseMenu === menu.id ? 'activeMenu' : ''" @click="clickBaseMenu(menu)"
:key="menu.name"> :key="menu.name">
<img :src="currentBaseMenu ===menu.id?menu.icon:menu.activeIcon" alt=""> {{ menu.name }} <img :src="currentBaseMenu === menu.id ? menu.icon : menu.activeIcon" alt=""> {{ menu.name }}
</li> </li>
</ul> </ul>
@ -54,17 +51,14 @@
<span class="title">{{ baseItem.title }}</span> <span class="title">{{ baseItem.title }}</span>
<p style="line-height: 2" class="description">{{ baseItem.description }}</p> <p style="line-height: 2" class="description">{{ baseItem.description }}</p>
<ul class="itemContentTag"> <ul class="itemContentTag">
<li v-for="(item,index) in baseItem.list" :key="index"> <li v-for="(item, index) in baseItem.list" :key="index">
<img :src="item.icon" alt=""><span v-if="item.name" <img :src="item.icon" alt=""><span v-if="item.name" :style="item.name === '高可靠' ? { width: '150px' } : {}"
:style="item.name === '高可靠' ? { width: '150px' } : {}" class="tagTitle">{{ item.name }}</span> <span class="tagContent">{{ item.content }}</span>
class="tagTitle">{{ item.name }}</span> <span
class="tagContent">{{ item.content }}</span>
</li> </li>
</ul> </ul>
<span class="basePrice"><span style="font-weight: bold;"></span><span <span class="basePrice"><span style="font-weight: bold;"></span><span
style="font-weight: bold;font-size: 48px">{{ baseItem.price }}</span> style="font-weight: bold;font-size: 48px">{{ baseItem.price }}</span>
<span <span style="color: #7A82A0">/{{ baseItem.price_unit }}</span>
style="color: #7A82A0">/{{ baseItem.price_unit }}</span>
<!-- <span--> <!-- <span-->
<!-- style="color: #7A82A0;text-decoration-line: line-through">{{ baseItem.pre_price }}/{{--> <!-- style="color: #7A82A0;text-decoration-line: line-through">{{ baseItem.pre_price }}/{{-->
<!-- baseItem.price_unit--> <!-- baseItem.price_unit-->
@ -72,50 +66,47 @@
</span> </span>
<div class="twoBtn"> <div class="twoBtn">
<div class="butStyle" @click="goBaiduFn(baseItem)">立即购买 <span <div class="butStyle" @click="goBaiduFn(baseItem)">立即购买 <span
style="display: inline-block;margin-left: 5px;color: white">></span> style="display: inline-block;margin-left: 5px;color: white">></span>
</div> </div>
<div v-show="false" class="lookDetailStyle">查看详情&nbsp;&nbsp;> <span <div v-show="false" class="lookDetailStyle">查看详情&nbsp;&nbsp;> <span
style="display: inline-block;margin-left: 5px;"></span> style="display: inline-block;margin-left: 5px;"></span>
</div> </div>
</div> </div>
<span class="discountStyle"><span style="font-size: 18px">官网折扣</span> <span <span class="discountStyle"><span style="font-size: 18px">官网折扣</span> <span style="font-size: 24px">{{
style="font-size: 24px">{{ baseItem.discount }}</span></span> baseItem.discount }}</span></span>
</div> </div>
</div> </div>
<div class="suan"> <div class="suan">
<div class="bg"></div> <div class="bg"></div>
<div class="itemTitle"> <div class="itemTitle">
<span class="topText"> <span>智算</span><span class="commonTextColor">未来征程</span></span> <span class="topText"> <span>智算</span><span class="commonTextColor">未来征程</span></span>
<span class="bottomText">多元异构 灵活短租</span> <span class="bottomText">多元异构 灵活短租</span>
</div> </div>
<ul class="myTab"> <ul class="myTab">
<li v-for="menu in suanMenu" :class="currentSuanMenu ===menu.id?'activeMenu':''" @click="clickSuanMenu(menu)" <li v-for="menu in suanMenu" :class="currentSuanMenu === menu.id ? 'activeMenu' : ''" @click="clickSuanMenu(menu)"
:key="menu.name"> :key="menu.name">
<img :src="currentSuanMenu ===menu.id?menu.icon:menu.activeIcon" alt=""> {{ menu.name }} <img :src="currentSuanMenu === menu.id ? menu.icon : menu.activeIcon" alt=""> {{ menu.name }}
</li> </li>
</ul> </ul>
<div class="itemContentSuan"> <div class="itemContentSuan">
<span class="title">{{ suanItem.title }}</span> <span class="title">{{ suanItem.title }}</span>
<ul class="itemContentTag"> <ul class="itemContentTag">
<li v-for="(item,index) in suanItem.list" :key="index"> <li v-for="(item, index) in suanItem.list" :key="index">
<img :src="item.icon" alt=""><span v-if="item.name" <img :src="item.icon" alt=""><span v-if="item.name" :style="item.name === '高可靠' ? { width: '150px' } : {}"
:style="item.name === '高可靠' ? { width: '150px' } : {}" class="tagTitle">{{ item.name }}</span> <span class="tagContent">{{ item.content }}</span>
class="tagTitle">{{ item.name }}</span> <span
class="tagContent">{{ item.content }}</span>
</li> </li>
</ul> </ul>
<span class="basePrice"><span style="font-weight: bold;"></span><span <span class="basePrice"><span style="font-weight: bold;"></span><span
style="font-weight: bold;font-size: 48px">{{ suanItem.price }}</span> style="font-weight: bold;font-size: 48px">{{ suanItem.price }}</span>
<span <span style="color: #7A82A0">/{{ suanItem.price_unit }}</span>
style="color: #7A82A0">/{{ suanItem.price_unit }}</span>
<!-- <span--> <!-- <span-->
<!-- style="color: #7A82A0;text-decoration-line: line-through">{{ suanItem.pre_price }}/{{--> <!-- style="color: #7A82A0;text-decoration-line: line-through">{{ suanItem.pre_price }}/{{-->
<!-- suanItem.price_unit--> <!-- suanItem.price_unit-->
@ -130,12 +121,12 @@
<!-- </span>--> <!-- </span>-->
<div style="margin-top: 10px!important;" class="twoBtn"> <div style="margin-top: 10px!important;" class="twoBtn">
<div class="butStyle" @click="openTalk">立即咨询 <span <div class="butStyle" @click="openTalk">立即咨询 <span
style="display: inline-block;margin-left: 5px;color: white">></span> style="display: inline-block;margin-left: 5px;color: white">></span>
</div> </div>
<div v-show="false" class="lookDetailStyle">查看详情&nbsp;&nbsp;> <span <div v-show="false" class="lookDetailStyle">查看详情&nbsp;&nbsp;> <span
style="display: inline-block;margin-left: 5px;"></span> style="display: inline-block;margin-left: 5px;"></span>
</div> </div>
@ -146,13 +137,13 @@
<div class="net"> <div class="net">
<div class="bg"></div> <div class="bg"></div>
<div class="itemTitle"> <div class="itemTitle">
<span class="topText"> <span>网织</span><span class="commonTextColor">智能经纬</span></span> <span class="topText"> <span>网织</span><span class="commonTextColor">智能经纬</span></span>
<span class="bottomText">云算网融合 让连接更简单</span> <span class="bottomText">云算网融合 让连接更简单</span>
</div> </div>
<ul class="myTab"> <ul class="myTab">
<li v-for="menu in netMenu" :class="currentNetMenu ===menu.id?'activeMenu':''" @click="clickNetMenu(menu)" <li v-for="menu in netMenu" :class="currentNetMenu === menu.id ? 'activeMenu' : ''" @click="clickNetMenu(menu)"
:key="menu.name"> :key="menu.name">
<img :src="currentBaseMenu ===menu.id?menu.icon:menu.activeIcon" alt=""> {{ menu.name }} <img :src="currentBaseMenu === menu.id ? menu.icon : menu.activeIcon" alt=""> {{ menu.name }}
</li> </li>
</ul> </ul>
@ -161,30 +152,29 @@
<p style="line-height: 2;margin-bottom: 15px" class="description">{{ netItem.description }}</p> <p style="line-height: 2;margin-bottom: 15px" class="description">{{ netItem.description }}</p>
<span class="subTitle">产品优势</span> <span class="subTitle">产品优势</span>
<ul s class="itemContentTag"> <ul s class="itemContentTag">
<li v-for="(item,index) in netItem.advantageList" :key="index"> <li v-for="(item, index) in netItem.advantageList" :key="index">
<img :src="item.icon" alt=""><span style="width: fit-content!important;" class="tagTitle">{{ <img :src="item.icon" alt=""><span style="width: fit-content!important;" class="tagTitle">{{
item.name item.name
}}</span> <span }}</span> <span class="tagContent">{{ item.content }}</span>
class="tagContent">{{ item.content }}</span>
</li> </li>
</ul> </ul>
<span class="subTitle">应用场景</span> <span class="subTitle">应用场景</span>
<ul class="netTagUl"> <ul class="netTagUl">
<li v-for="(i,d) in netItem.tagList" :key="d">{{ i.name }}</li> <li v-for="(i, d) in netItem.tagList" :key="d">{{ i.name }}</li>
</ul> </ul>
<div class="twoBtn"> <div class="twoBtn">
<div @click="openTalk" class="butStyle">立即咨询 <span <div @click="openTalk" class="butStyle">立即咨询 <span
style="display: inline-block;margin-left: 5px;color: white">></span> style="display: inline-block;margin-left: 5px;color: white">></span>
</div> </div>
<div @click="goDetail(netItem.id)" class="lookDetailStyle">查看详情&nbsp;&nbsp;> <span <div @click="goDetail(netItem.id)" class="lookDetailStyle">查看详情&nbsp;&nbsp;> <span
style="display: inline-block;margin-left: 5px;"></span> style="display: inline-block;margin-left: 5px;"></span>
</div> </div>
</div> </div>
</div> </div>
<div style="margin-top: 80px;margin-bottom: 50px" class="itemTitle"> <div style="margin-top: 80px;margin-bottom: 50px" class="itemTitle">
<span class="topText"> <span></span><span class="commonTextColor">合作伙伴</span></span> <span class="topText"> <span></span><span class="commonTextColor">合作伙伴</span></span>
</div> </div>
<div style="max-width:1400px;padding: 15px;width: 100%;background: white;border-radius: 16px;margin-bottom: 30px"> <div style="max-width:1400px;padding: 15px;width: 100%;background: white;border-radius: 16px;margin-bottom: 30px">
<logo-g></logo-g> <logo-g></logo-g>
@ -195,7 +185,7 @@
<div class="bg"></div> <div class="bg"></div>
<div style="margin-top: 140px" class="itemTitle"> <div style="margin-top: 140px" class="itemTitle">
<span class="topText"> <span>算力</span><span class="commonTextColor">资源</span></span> <span class="topText"> <span>算力</span><span class="commonTextColor">资源</span></span>
</div> </div>
<div class="mapBox"> <div class="mapBox">
<div style="display: flex;justify-content: space-around;width: 100%;"> <div style="display: flex;justify-content: space-around;width: 100%;">
@ -227,8 +217,8 @@
<li> <li>
<div style="display: flex;flex-direction: column;align-items: flex-start"> <div style="display: flex;flex-direction: column;align-items: flex-start">
<span class="numberStyle">8 <span class="numberStyle">8
<span class="mapTag">+</span> <span class="mapTag">+</span>
</span> </span>
<span style="color: #222F60;font-size: 18px;margin-top: 5px">算力中心接入</span> <span style="color: #222F60;font-size: 18px;margin-top: 5px">算力中心接入</span>
</div> </div>
</li> </li>
@ -251,16 +241,16 @@
<li> <li>
<div style="display: flex;flex-direction: column;align-items: flex-start"> <div style="display: flex;flex-direction: column;align-items: flex-start">
<span class="numberStyle">2000 <span class="numberStyle">2000
<span style="right: -35px" class="mapTag">P+</span> <span style="right: -35px" class="mapTag">P+</span>
</span> </span>
<span style="color: #222F60;font-size: 18px;margin-top: 5px">算力</span> <span style="color: #222F60;font-size: 18px;margin-top: 5px">算力</span>
</div> </div>
</li> </li>
<li> <li>
<div style="display: flex;flex-direction: column;align-items: flex-start"> <div style="display: flex;flex-direction: column;align-items: flex-start">
<span class="numberStyle">10 <span class="numberStyle">10
<span style="right: -35px" class="mapTag">ms</span> <span style="right: -35px" class="mapTag">ms</span>
</span> </span>
<span style="color: #222F60;font-size: 18px;margin-top: 5px">实时算力响应</span> <span style="color: #222F60;font-size: 18px;margin-top: 5px">实时算力响应</span>
</div> </div>
</li> </li>
@ -269,8 +259,8 @@
<li> <li>
<div style="display: flex;flex-direction: column;align-items: flex-start"> <div style="display: flex;flex-direction: column;align-items: flex-start">
<span class="numberStyle">100,000 <span class="numberStyle">100,000
<span class="mapTag">+</span> <span class="mapTag">+</span>
</span> </span>
<span style="color: #222F60;font-size: 18px;margin-top: 5px">注册用户</span> <span style="color: #222F60;font-size: 18px;margin-top: 5px">注册用户</span>
</div> </div>
</li> </li>
@ -289,8 +279,8 @@
<el-input v-model="phone" placeholder="请输入您的手机号" class="custom-input"> <el-input v-model="phone" placeholder="请输入您的手机号" class="custom-input">
<template #append> <template #append>
<span @click="$router.push({ <span @click="$router.push({
path:'/login', path: '/login',
phone phone
})" class="experience-btn">立即体验</span> })" class="experience-btn">立即体验</span>
</template> </template>
</el-input> </el-input>
@ -299,21 +289,13 @@
<Talk></Talk> <Talk></Talk>
<el-dialog <el-dialog title="" :visible.sync="mesDialog" custom-class="msgDialog" :before-close="handleClose" width="30%">
title=""
:visible.sync="mesDialog"
custom-class="msgDialog"
:before-close="handleClose"
width="30%"
>
<span class="title">官方申明</span> <span class="title">官方申明</span>
<p>尊敬用户</p> <p>尊敬用户</p>
<p>您好~~ </p> <p>您好~~ </p>
<p>本平台公示的所有产品折扣活动均真实有效价格体系严格遵循公开透明原则 </p> <p>本平台公示的所有产品折扣活动均真实有效价格体系严格遵循公开透明原则 </p>
<p> 特别说明<span <p> 特别说明<span style="color: #7A82A0!important;">云服务产品页面所示价格属参考性标价实际交易金额须以资源清单订单页面为准</span>
style="color: #7A82A0!important;">云服务产品页面所示价格属参考性标价实际交易金额须以资源清单订单页面为准</span>
</p> </p>
<p>请您知悉上述条款并放心进行购买决策如有任何疑问可随时联系平台官方客服我们将为您详细说明</p> <p>请您知悉上述条款并放心进行购买决策如有任何疑问可随时联系平台官方客服我们将为您详细说明</p>
<div style="margin-top: 85px" class="footerBox"> <div style="margin-top: 85px" class="footerBox">
@ -336,15 +318,15 @@
</template> </template>
<script> <script>
import Vue from 'vue' import Vue from 'vue'
import {reqHotProduct} from "@/api/newHome"; import { reqHotProduct } from "@/api/newHome";
import Talk from "@/views/homePage/dialog/talk/index.vue"; import Talk from "@/views/homePage/dialog/talk/index.vue";
import LogoG from "@/views/homePage/mainPage/logoG/index.vue"; import LogoG from "@/views/homePage/mainPage/logoG/index.vue";
import {mapState} from "vuex"; import { mapState } from "vuex";
import IndexTwo from "@/views/homePage/mainPage/logoG/indexTwo.vue"; import IndexTwo from "@/views/homePage/mainPage/logoG/indexTwo.vue";
export default Vue.extend({ export default Vue.extend({
name: "mainPage", name: "mainPage",
components: {IndexTwo, LogoG, Talk}, components: { IndexTwo, LogoG, Talk },
data() { data() {
return { return {
mesDialog: localStorage.getItem('showMsg') !== '1', // mesDialog: localStorage.getItem('showMsg') !== '1', //
@ -575,11 +557,15 @@ export default Vue.extend({
font-size: 64px; font-size: 64px;
.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;
/* 确保渐变生效 */
} }
} }
@ -615,11 +601,15 @@ export default Vue.extend({
} }
.commonTextColor { .commonTextColor {
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;
/* 确保渐变生效 */
font-weight: bold; font-weight: bold;
} }
@ -650,11 +640,15 @@ export default Vue.extend({
} }
.price { .price {
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;
/* 确保渐变生效 */
font-weight: bold; font-weight: bold;
.bigText { .bigText {
@ -737,10 +731,12 @@ export default Vue.extend({
z-index: 10; z-index: 10;
padding-left: 50px; padding-left: 50px;
padding-top: 50px; padding-top: 50px;
color: #222F60;; color: #222F60;
;
* { * {
color: #222F60;; color: #222F60;
;
} }
padding-bottom: 25px; padding-bottom: 25px;
@ -834,11 +830,15 @@ export default Vue.extend({
} }
.lookDetailStyle { .lookDetailStyle {
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;
/* 确保渐变生效 */
font-weight: bold; font-weight: bold;
margin-left: 85px; margin-left: 85px;
border-radius: 12px; border-radius: 12px;
@ -926,11 +926,13 @@ export default Vue.extend({
z-index: 10; z-index: 10;
padding-left: 50px; padding-left: 50px;
padding-top: 50px; padding-top: 50px;
color: #222F60;; color: #222F60;
;
* { * {
color: #222F60;; color: #222F60;
;
} }
width: 1400px; width: 1400px;
@ -1026,11 +1028,15 @@ export default Vue.extend({
} }
.lookDetailStyle { .lookDetailStyle {
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;
/* 确保渐变生效 */
font-weight: bold; font-weight: bold;
margin-left: 85px; margin-left: 85px;
@ -1097,10 +1103,12 @@ export default Vue.extend({
z-index: 10; z-index: 10;
padding-left: 50px; padding-left: 50px;
padding-top: 50px; padding-top: 50px;
color: #222F60;; color: #222F60;
;
* { * {
color: #222F60;; color: #222F60;
;
} }
padding-bottom: 25px; padding-bottom: 25px;
@ -1195,11 +1203,15 @@ export default Vue.extend({
} }
.lookDetailStyle { .lookDetailStyle {
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;
/* 确保渐变生效 */
font-weight: bold; font-weight: bold;
margin-left: 85px; margin-left: 85px;
border-radius: 12px; border-radius: 12px;
@ -1300,21 +1312,30 @@ export default Vue.extend({
.numberStyle { .numberStyle {
position: relative; position: relative;
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;
/* 确保渐变生效 */
.mapTag { .mapTag {
font-size: 24px; font-size: 24px;
position: absolute; position: absolute;
right: -20px; right: -20px;
top: 0; top: 0;
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;
/* 确保渐变生效 */
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1753777468651" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6851" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14"><path d="M566.61333333 480.32426667c175.3088 16.384 309.6576 109.7728 309.6576 222.27626666 0 124.5184-163.29386667 225.00693333-364.81706666 225.00693334S147.18293333 827.11893333 147.18293333 703.14666667c0-102.67306667 111.4112-189.50826667 264.32853334-216.2688v103.76533333c0 20.20693333 8.192 39.86773333 22.9376 54.0672 14.7456 14.19946667 34.4064 22.39146667 54.61333333 22.39146667l6.5536-0.54613334c39.86773333-3.2768 70.4512-36.0448 70.99733333-76.45866666V480.32426667z" p-id="6852" fill="#ffffff"></path><path d="M492.88533333 61.44L801.45066667 243.3024c9.8304 5.46133333 12.56106667 18.0224 7.09973333 27.30666667-2.18453333 4.36906667-6.00746667 7.09973333-10.92266667 8.73813333l-277.43573333 97.21173333v207.53066667c0 15.83786667-13.1072 28.94506667-28.94506667 28.94506667s-28.94506667-12.56106667-28.94506666-28.94506667V78.91626667c0-7.09973333 3.82293333-14.19946667 10.37653333-17.47626667 6.00746667-3.82293333 13.65333333-3.82293333 20.20693333 0z" p-id="6853" fill="#ffffff"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1753777558445" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10925" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14"><path d="M781.1072 971.0592H249.2928c-90.3168 0-163.84-73.5232-163.84-163.84V402.688c0-90.3168 73.5232-163.84 163.84-163.84h531.8144c90.3168 0 163.84 73.5232 163.84 163.84v404.5312c0 90.3168-73.472 163.84-163.84 163.84zM249.2928 300.288c-56.4736 0-102.4 45.9264-102.4 102.4v404.5312c0 56.4736 45.9264 102.4 102.4 102.4h531.8144c56.4736 0 102.4-45.9264 102.4-102.4V402.688c0-56.4736-45.9264-102.4-102.4-102.4H249.2928z" fill="#ffffff" p-id="10926"></path><path d="M691.968 457.8816c-16.9472 0-30.72-13.7728-30.72-30.72V262.5024c0-81.664-66.4064-148.0704-148.0704-148.0704S365.1072 180.8384 365.1072 262.5024v164.6592c0 16.9472-13.7728 30.72-30.72 30.72s-30.72-13.7728-30.72-30.72V262.5024c0-115.5584 94.0032-209.5104 209.5104-209.5104s209.5104 94.0032 209.5104 209.5104v164.6592c0 16.9472-13.7216 30.72-30.72 30.72z" fill="#ffffff" p-id="10927"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1753777515332" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7923" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14"><path d="M316.034305 645.090181c-16.969487 0-30.789237-13.821797-30.789237-30.79026 0-16.991999 13.820774-30.841426 30.789237-30.841426l376.865253 0c16.993023 0 30.813796 13.847379 30.813796 30.841426 0 16.968463-13.820774 30.79026-30.813796 30.79026L316.034305 645.090181 316.034305 645.090181zM311.444776 811.624055c-16.995069 0-30.815843-13.82282-30.815843-30.81482 0-16.968463 13.81975-30.815843 30.815843-30.815843l376.838647 0c16.994046 0 30.81482 13.847379 30.81482 30.815843 0 16.991999-13.820774 30.81482-30.81482 30.81482L311.444776 811.624055 311.444776 811.624055zM144.125003 1024c-53.214992 0-83.21526-33.604352-83.21526-84.266196L60.909743 116.042923c0-48.669466 24.882714-90.86652 85.261872-90.86652l530.546402 0c12.982686 0 24.206308 6.936993 30.027897 18.554588 5.796006 11.631922 4.64274 24.785499-3.173273 35.168987-6.294356 8.419763-16.33913 13.45443-26.854625 13.45443l-442.508299 0c-19.617803 0-35.196617 0-47.888683 0l-25.255197 0c-7.945973 0-14.319124 0-19.984147 0l-13.034874-0.039909 0 864.495216 771.300674 0-0.024559-13.007245c-0.077771-19.433608-0.052189-33.910321-0.026606-52.241828l0-0.788969c0-11.120268 0.026606-23.813359 0.026606-40.1003L899.322931 361.549429c0-10.476609 4.982478-20.494777 13.323447-26.802436 6.033413-4.510734 12.955056-6.872524 20.062941-6.872524 16.231683 0 33.778314 12.863982 33.778314 33.662681l0 582.291926c0 25.308409-5.506411 44.662199-16.338107 57.485249-12.668531 15.030321-34.146705 22.686698-63.832817 22.686698L144.125003 1024 144.125003 1024zM824.397475 53.905724l7.867178-9.677407c6.531763-8.063653 15.448854-19.092847 20.928658-25.110911 9.31004-10.215666 22.107507-15.631002 37.004799-15.631002 9.335622 0 17.333784 2.085498 22.421662 3.843538 16.944927 5.820566 39.312354 25.387203 49.907667 43.612287 11.434423 19.709901 11.33107 39.969317-0.261966 55.599296-9.389858 12.745279-16.809851 22.212908-19.590174 25.675776l-7.107885 8.955976L824.397475 53.905724 824.397475 53.905724zM614.432439 472.129944c-6.242168 0-11.304464-1.952468-15.080463-5.810333-4.771677-4.889357-6.897084-12.285814-6.661723-23.248494 0.419556-18.712177 10.490935-77.614705 14.320147-90.099041 2.883677-9.388834 6.39771-17.873066 14.160511-28.691459 11.199063-15.552208 150.09139-199.605084 177.812755-236.347916l7.082302-9.360182L917.077294 165.785837l-6.738471 9.009188c-39.235606 52.267411-167.714769 223.508493-174.953637 232.766344-6.374174 8.157797-14.923898 16.194844-22.843264 21.4925-16.548908 11.106965-69.654406 36.911678-87.146802 41.475624C621.276311 471.606011 617.683483 472.129944 614.432439 472.129944L614.432439 472.129944 614.432439 472.129944z" fill="#ffffff" p-id="7924"></path></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,182 @@
<template>
<!-- 产品列表 -->
<ul class="productListContent" style="padding: 16px;">
<li v-if="type === 'homePage'" class="product-item" v-for="item in productList" :key="item.id">
<div class="product-image">
<img :src="item.img" style="min-width: 278px;min-height: 185px;" :alt="item.name">
</div>
<div class="product-info">
<h3 class="product-name">{{ item.name }}</h3>
<div class="product-specs">
<div class="spec-item">
<span class="spec-label">CPU:</span>
<span class="spec-value">{{ item.cpu }}</span>
</div>
<div class="spec-item">
<span class="spec-label">内存:</span>
<span class="spec-value">{{ item.memory }}</span>
</div>
<div class="spec-item">
<span class="spec-label">GPU:</span>
<span class="spec-value">{{ item.gpu }}</span>
</div>
<div class="spec-item">
<span class="spec-label">系统盘:</span>
<span class="spec-value">{{ item.sys_disk }}</span>
</div>
<div class="spec-item">
<span class="spec-label">数据盘:</span>
<span class="spec-value">{{ item.data_disk }}</span>
</div>
<div class="spec-item">
<span class="spec-label">网卡:</span>
<span class="spec-value">{{ item.net_card }}</span>
</div>
</div>
<div class="product-price">
<span class="price">¥{{ item.price }}</span>
<span class="price-unit">// (可短租)</span>
</div>
<button class="consult-btn">立即咨询</button>
</div>
</li>
<li>
</li>
</ul>
</template>
<script>
export default {
name: 'productCard',
props: {
productList: {
type: Array,
default: () => []
},
type: {
type: String,
default: 'homePage'
}
}
}
</script>
<style scoped lang="scss">
.productListContent {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
padding: 16px;
max-width: 1400px;
margin: 0 auto;
background-color: white;
border: 1px solid red;
.product-item {
width: 100%;
background: #F7F9FD;
border-radius: 12px;
// box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 24px;
margin-bottom: 20px;
transition: all 0.3s ease;
// &:hover {
// transform: translateY(-2px);
// box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
// }
.product-image {
margin-bottom: 20px;
text-align: center;
img {
width: 100%;
max-width: 300px;
height: 200px;
object-fit: cover;
border-radius: 8px;
background: #1a1a1a;
}
}
.product-info {
.product-name {
font-size: 24px;
font-weight: bold;
color: #333;
margin: 0 0 16px 0;
text-align: start;
}
.product-specs {
margin-bottom: 20px;
.spec-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
.spec-label {
color: #222F60 !important;
font-size: 14px;
font-weight: 900;
min-width: 80px;
}
.spec-value {
color: #666;
font-size: 14px;
text-align: right;
flex: 1;
margin-left: 16px;
}
}
}
.product-price {
text-align: start;
margin-bottom: 20px;
.price {
font-size: 28px;
font-weight: bold;
color: #e1251b;
margin-right: 8px;
}
.price-unit {
font-size: 14px;
color: #666;
}
}
.consult-btn {
width: 100%;
background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%);
color: white;
border: none;
padding: 12px 24px;
border-radius: 8px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(39, 90, 255, 0.3);
}
}
}
}
}
</style>

View File

@ -0,0 +1,19 @@
export const buildCaTree = (data,parentid=null) => {
const tree = [];
for (const item of data) {
if (item.parentid === parentid) {
const children = buildCaTree(data, item.id);
let i = {
label:item.product_category,
value:item.id,
cart_flag:item.cart_flag
}
if (children.length > 0) {
i.children = children;
}
tree.push(i);
}
}
return tree;
};

View File

@ -0,0 +1,904 @@
<template>
<div class="form-container">
<el-form :model="form" :rules="rules" ref="form" label-width="120px" class="two-column-form">
<!-- 商品图片 - 单独一行 -->
<el-form-item label="商品图片" prop="img" class="full-width">
<div class="upload-area" @click="!selectedImage && triggerFileInput()">
<input ref="fileInput" type="file" accept="image/*" @change="handleFileChange"
style="display: none;">
<div v-if="!selectedImage" class="upload-placeholder">
<i class="el-icon-plus" style="font-size: 40px;"></i>
</div>
<div v-else class="image-preview">
<img :src="selectedImage" alt="预览图" @click="previewImage">
<div class="preview-overlay">
<i class="el-icon-zoom-in"></i>
</div>
</div>
</div>
<!-- 图片操作按钮 -->
<div v-if="selectedImage" class="image-actions">
<el-button size="mini" type="primary" @click="previewImage">预览图片</el-button>
<el-button size="mini" type="primary" @click="openCropper">裁剪图片</el-button>
<el-button size="mini" type="danger" @click="removeImage">删除</el-button>
</div>
</el-form-item>
<el-form-item label="商品名称" prop="product_name" required>
<el-input v-model="form.product_name" placeholder="请输入商品名称"></el-input>
</el-form-item>
<!-- 表单项直接排列 -->
<div class="form-row">
<el-form-item label="所属类别" prop="product_category" required class="form-item-half">
<el-cascader v-model="form.product_category" style="width: 100%;" :options="typeList"
@change="handleChange"></el-cascader>
</el-form-item>
<el-form-item label="企业名称" prop="company_name" required class="form-item-half">
<el-input v-model="form.company_name" placeholder="请输入企业名称"></el-input>
</el-form-item>
</div>
<el-form-item label="公司类别" prop="company_type" required class="full-width">
<el-checkbox-group v-model="form.company_type">
<el-checkbox v-for="item in company_category_list" :key="item.value"
:label="item.label"></el-checkbox>
</el-checkbox-group>
</el-form-item>
<div class="form-row">
<el-form-item label="联系人" prop="contact_person" required class="form-item-half">
<el-input v-model="form.contact_person" placeholder="请输入联系人姓名"></el-input>
</el-form-item>
<el-form-item label="职务" prop="job_title" class="form-item-half">
<el-input v-model="form.job_title" placeholder="请输入职务"></el-input>
</el-form-item>
</div>
<div class="form-row">
<el-form-item label="手机号码" prop="phone_number" required class="form-item-half">
<el-input v-model="form.phone_number" placeholder="请输入手机号码"></el-input>
</el-form-item>
<el-form-item label="邮箱" prop="email" class="form-item-half">
<el-input v-model="form.email" placeholder="请输入邮箱地址"></el-input>
</el-form-item>
</div>
<div class="form-row" v-if="current_product_category.cart_flag=='1'">
<el-form-item label="CPU" class="form-item-half">
<el-input v-model="form.cpu" placeholder="请输入CPU规格"></el-input>
</el-form-item>
<el-form-item label="内存" class="form-item-half">
<el-input v-model="form.memory" placeholder="请输入内存规格"></el-input>
</el-form-item>
</div>
<div class="form-row" v-if="current_product_category.cart_flag=='1'">
<el-form-item label="GPU" class="form-item-half">
<el-input v-model="form.gpu" placeholder="请输入GPU规格"></el-input>
</el-form-item>
<el-form-item label="系统盘" class="form-item-half">
<el-input v-model="form.sys_disk" placeholder="请输入系统盘规格"></el-input>
</el-form-item>
</div>
<div class="form-row" v-if="current_product_category.cart_flag=='1'">
<el-form-item label="数据盘" class="form-item-half">
<el-input v-model="form.data_disk" placeholder="请输入数据盘规格"></el-input>
</el-form-item>
<el-form-item label="网卡" prop="net_card" required class="form-item-half">
<el-input v-model="form.net_card" placeholder="请输入网卡规格"></el-input>
</el-form-item>
</div>
<div class="form-row">
<el-form-item label="价格" prop="price" required class="form-item-half">
<el-input v-model="form.price" placeholder="请输入价格: 示例: xx元/台/月"></el-input>
</el-form-item>
</div>
<el-form-item label="商品概述" prop="requirement_summary" required class="full-width">
<el-input type="textarea" v-model="form.requirement_summary" :rows="6" placeholder="请输入商品概述"></el-input>
</el-form-item>
<el-form-item label="相关参数" prop="related_parameters" class="full-width">
<el-input type="textarea" v-model="form.related_parameters" :rows="6" placeholder="请输入相关参数"></el-input>
</el-form-item>
<el-form-item label="应用场景" prop="application_scenario" class="full-width">
<el-input type="textarea" v-model="form.application_scenario" :rows="6"
placeholder="请输入应用场景"></el-input>
</el-form-item>
<!-- 提交按钮 -->
<div class="form-actions">
<el-button type="primary" @click="submitForm" size="large">提交</el-button>
<el-button @click="resetForm" size="large">重置</el-button>
<!-- <el-button type="info" @click="getBinaryData" size="large">获取二进制数据</el-button> -->
</div>
</el-form>
<!-- 图片裁剪弹窗 -->
<el-dialog title="图片裁剪" :visible.sync="showCropper" width="60%" top="5vh" :before-close="closeCropper"
custom-class="cropper-dialog" :append-to-body="true" :modal-append-to-body="true" :z-index="9999"
:destroy-on-close="true">
<div class="cropper-container">
<vue-cropper ref="cropper" :img="cropperImage" :outputSize="1" :info="true" :full="true" :canMove="true"
:canMoveBox="true" :original="false" :autoCrop="true" :autoCropWidth="300" :autoCropHeight="200"
:fixed="false" :centerBox="true" :infoTrue="true" :high="true" mode="cover" :maxImgSize="2000"
:enlargeImg="1" :limitMinSize="[100, 100]" @realTime="realTime" />
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="closeCropper">取消</el-button>
<el-button type="primary" @click="cropImage">确认裁剪</el-button>
</div>
</el-dialog>
<!-- 图片预览弹窗 -->
<el-dialog title="图片预览" :visible.sync="showPreview" width="50%" center top="5vh" :append-to-body="true"
:modal-append-to-body="true" :z-index="9998" :destroy-on-close="true" custom-class="preview-dialog">
<div class="preview-container">
<img :src="selectedImage" alt="预览图" class="preview-image">
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="showPreview = false">关闭</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { VueCropper } from 'vue-cropper'
import { buildCaTree } from './buildCaTree'
import { reqGetProductCategorySearch, reqPublishProductAdd, reqCompanyCategorySearch } from '@/api/ncmatch'
export default {
name: 'sendProduct',
components: {
VueCropper
},
data() {
return {
company_category_list: [],
typeList: [
],
current_product_category: {},//
selectedImage: null,
showCropper: false,
showPreview: false,
cropperImage: '',
inputVisible: false,
inputValue: '',
form: {
url_link: window.location.href,
img: null, //
product_name: "",//
product_category: "",//
company_name: "",//
company_type: [],//
contact_person: "",//
job_title: "",//
phone_number: "",//
email: "",//
cpu: "",//CPU
memory: "",//
gpu: "",//GPU
sys_disk: "",//
data_disk: "",//
net_card: "",//
priority: "",//
price: "",//
label: "",//
requirement_summary: "",//
related_parameters: "",//
application_scenario: "",//
},
rules: {
product_category: [
{ required: true, message: '请选择所属类别', trigger: 'change' }
],
company_name: [
{ required: true, message: '请输入企业名称', trigger: 'blur' }
],
company_type: [
{ required: true, message: '请选择公司类别', trigger: 'change' }
],
contact_person: [
{ required: true, message: '请输入联系人', trigger: 'blur' }
],
phone_number: [
{ required: true, message: '请输入手机号码', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
],
email: [
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
],
cpu: [
{ required: true, message: '请输入CPU规格', trigger: 'blur' }
],
memory: [
{ required: true, message: '请输入内存规格', trigger: 'blur' }
],
gpu: [
{ required: true, message: '请输入GPU规格', trigger: 'blur' }
],
sys_disk: [
{ required: true, message: '请输入系统盘规格', trigger: 'blur' }
],
data_disk: [
{ required: true, message: '请输入数据盘规格', trigger: 'blur' }
],
net_card: [
{ required: true, message: '请输入网卡规格', trigger: 'blur' }
],
price: [
{ required: true, message: '请输入价格', trigger: 'blur' }
],
img: [
{
required: true,
validator: (rule, value, callback) => {
if (!value) {
callback(new Error('请上传商品图片'))
} else {
callback()
}
},
trigger: 'change'
}
],
requirement_summary: [
{ required: true, message: '请输入商品概述', trigger: 'blur' }
]
}
}
},
created() {
this.init_product_category()
this.init_company_category()
},
methods: {
init_company_category() {
reqCompanyCategorySearch({ userid: '9KVhsVCJsW_29q3hRhMAr' }).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
})
}
}
})
},
// base64Blob
base64ToBlob(base64, mimeType = 'image/jpeg') {
const byteString = atob(base64.split(',')[1]);
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], { type: mimeType });
},
init_product_category() {
reqGetProductCategorySearch({ userid: '9KVhsVCJsW_29q3hRhMAr' }).then(res => {
if (res.status) {
this.typeList = buildCaTree(res.data)
console.log("2@@", this.typeList);
}
})
},
//
triggerFileInput() {
this.$refs.fileInput.click()
},
//
handleFileChange(event) {
const file = event.target.files[0]
if (file) {
//
if (file.size > 5 * 1024 * 1024) {
this.$message.error('图片大小不能超过5MB')
return
}
//
if (!file.type.startsWith('image/')) {
this.$message.error('请选择图片文件')
return
}
console.log('选择的文件:', file)
console.log('文件类型:', file.type)
console.log('文件大小:', file.size)
// img
this.form.img = file
// URL
const imageUrl = URL.createObjectURL(file)
this.selectedImage = imageUrl
console.log('保存到 form.img:', this.form.img)
this.$refs.form.validateField('img')
}
},
//
openCropper() {
if (this.form.img) {
// Filebase64
const reader = new FileReader()
reader.onload = (e) => {
this.cropperImage = e.target.result
this.showCropper = true
// DOM
this.$nextTick(() => {
if (this.$refs.cropper) {
this.$refs.cropper.refresh()
}
// dialog
setTimeout(() => {
const dialogWrapper = document.querySelector('.cropper-dialog')
if (dialogWrapper) {
dialogWrapper.style.zIndex = '9999'
}
}, 100)
})
}
reader.readAsDataURL(this.form.img)
}
},
//
closeCropper() {
this.showCropper = false
this.cropperImage = ''
},
//
realTime(data) {
//
console.log('裁剪实时数据:', data)
},
//
cropImage() {
this.$refs.cropper.getCropData((data) => {
// data base64
// base64Blob
const blob = this.base64ToBlob(data)
this.form.img = blob
// URL
this.selectedImage = URL.createObjectURL(blob)
this.$message.success('图片裁剪成功!')
this.closeCropper()
})
},
//
removeImage() {
// URL
if (this.selectedImage) {
URL.revokeObjectURL(this.selectedImage)
}
this.selectedImage = null
this.form.img = null
this.$refs.fileInput.value = ''
this.$refs.form.validateField('img')
},
//
previewImage() {
if (this.selectedImage) {
this.showPreview = true
}
},
//
removeTag(tag) {
this.form.companyTags.splice(this.form.companyTags.indexOf(tag), 1)
},
showInput() {
this.inputVisible = true
this.$nextTick(_ => {
this.$refs.saveTagInput.$refs.input.focus()
})
},
handleInputConfirm() {
let inputValue = this.inputValue
if (inputValue) {
this.form.companyTags.push(inputValue)
}
this.inputVisible = false
this.inputValue = ''
},
//
submitForm() {
this.$refs.form.validate((valid) => {
if (valid) {
console.log('表单数据:', this.form)
let formdata = new FormData();
for (let key in this.form) {
formdata.append(key, this.form[key]);
}
reqPublishProductAdd(formdata).then(res => {
if (res.status) {
this.$emit('success');
this.$message.success('添加产品成功!')
}
})
} else {
this.$message.error('请完善表单信息')
}
})
},
//
resetForm() {
// URL
if (this.selectedImage) {
URL.revokeObjectURL(this.selectedImage)
}
this.$refs.form.resetFields()
this.selectedImage = null
this.form.img = null
this.$refs.fileInput.value = ''
},
//
handleChange(value) {
console.log('选中的id数组:', value)
//
function findNodeByValue(options, value) {
for (let item of options) {
if (item.value === value) return item;
if (item.children) {
const found = findNodeByValue(item.children, value);
if (found) return found;
}
}
return null;
}
const currentId = value[value.length - 1];
const currentNode = findNodeByValue(this.typeList, currentId);
console.log('当前节点对象:', currentNode);
this.current_product_category = currentNode
},
//
getBinaryData() {
console.log('form.img:', this.form.img)
console.log('form.img 类型:', typeof this.form.img)
console.log('form.img 构造函数:', this.form.img?.constructor?.name)
if (this.form.img) {
console.log('二进制数据 (Blob/File):', this.form.img)
console.log('文件大小:', this.form.img.size, '字节')
console.log('文件类型:', this.form.img.type)
// BlobFile
const file = new File([this.form.img], 'cropped-image.jpg', {
type: this.form.img.type
})
console.log('File对象:', file)
// BlobArrayBuffer
const reader = new FileReader()
reader.onload = (e) => {
const arrayBuffer = e.target.result
console.log('ArrayBuffer:', arrayBuffer)
}
reader.readAsArrayBuffer(this.form.img)
return this.form.img
} else {
console.log('没有二进制数据')
return null
}
}
}
}
</script>
<style scoped lang="scss">
.form-container {
padding: 20px;
max-width: 1000px;
margin: 0 auto;
.form-row {
display: flex;
gap: 20px;
margin-bottom: 20px;
.form-item-half {
flex: 1;
min-width: 0;
/* 防止flex项目溢出 */
}
}
.full-width {
width: 100%;
margin-bottom: 20px;
}
//
.el-form-item {
margin-bottom: 18px;
&.form-item-half {
margin-bottom: 0;
}
&.full-width {
margin-bottom: 20px;
}
}
.form-actions {
text-align: center;
padding: 20px 0;
background: #fff;
border-radius: 8px;
}
.tags-container {
.el-tag {
margin-right: 8px;
margin-bottom: 8px;
}
.tag-input {
width: 90px;
margin-right: 8px;
vertical-align: bottom;
}
.button-new-tag {
margin-left: 8px;
height: 32px;
line-height: 30px;
padding-top: 0;
padding-bottom: 0;
}
}
.upload-area {
display: flex;
align-items: center;
justify-content: flex-start;
overflow: hidden;
width: 80px;
height: 80px;
border: 2px dashed #ddd;
border-radius: 8px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
background: #fafafa;
display: flex;
align-items: center;
justify-content: center;
&:hover {
border-color: #409EFF;
background: #f0f9ff;
}
.upload-placeholder {
width: 100%;
height: 100%;
border: 1px solid #ddd;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
i {
color: #999;
display: block;
}
p {
margin: 5px 0;
color: #666;
&.upload-tip {
font-size: 12px;
color: #999;
}
}
}
.image-preview {
position: relative;
cursor: pointer;
img {
max-width: 100%;
max-height: 300px;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
transform: scale(1.05);
}
}
.preview-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
border-radius: 8px;
i {
color: white;
font-size: 24px;
}
}
&:hover .preview-overlay {
opacity: 1;
}
.image-actions {
margin-top: 15px;
display: flex;
gap: 10px;
justify-content: center;
}
}
.image-actions {
margin-top: 10px;
display: flex;
gap: 8px;
justify-content: center;
}
}
}
//
.cropper-dialog {
z-index: 9999 !important;
.el-dialog {
z-index: 9999 !important;
}
.el-dialog__wrapper {
z-index: 9999 !important;
}
.el-dialog__mask {
z-index: 9998 !important;
}
.cropper-container {
height: 350px;
display: flex;
justify-content: center;
align-items: center;
background: #f5f5f5;
border-radius: 8px;
padding: 20px;
}
.dialog-footer {
text-align: center;
padding: 20px 0;
}
}
//
.preview-dialog {
z-index: 9998 !important;
.el-dialog {
z-index: 9998 !important;
}
.el-dialog__wrapper {
z-index: 9998 !important;
}
.el-dialog__mask {
z-index: 9997 !important;
}
.preview-container {
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
background: #f5f5f5;
border-radius: 8px;
min-height: 300px;
.preview-image {
max-width: 100%;
max-height: 500px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
}
.dialog-footer {
text-align: center;
padding: 20px 0;
}
}
//
:global(.vue-cropper) {
.cropper-container {
width: 100% !important;
height: 100% !important;
}
.cropper-wrap-box {
width: 100% !important;
height: 100% !important;
}
.cropper-canvas {
width: 100% !important;
height: 100% !important;
}
.cropper-view-box {
border: 1px solid #fff;
box-shadow: 0 0 0 1px #39f;
}
.cropper-face {
background-color: rgba(39, 90, 255, 0.1);
}
.cropper-line {
background-color: #39f;
}
.cropper-point {
background-color: #39f;
border: 1px solid #fff;
width: 8px;
height: 8px;
}
}
// dialog
:global(.el-dialog__wrapper) {
&.cropper-dialog {
z-index: 9999 !important;
}
}
:global(.el-dialog) {
&.cropper-dialog {
z-index: 9999 !important;
}
}
// dialog
:global(.cropper-dialog) {
z-index: 9999 !important;
.el-dialog {
z-index: 9999 !important;
}
.el-dialog__wrapper {
z-index: 9999 !important;
}
.el-dialog__mask {
z-index: 9998 !important;
}
}
//
:deep(.vue-cropper) {
.cropper-view-box {
border-radius: 0;
}
.cropper-face {
background-color: rgba(39, 90, 255, 0.1);
}
}
//
:deep(.vue-cropper) {
width: 100% !important;
height: 100% !important;
position: relative;
.cropper-container {
width: 100% !important;
height: 100% !important;
position: relative;
}
.cropper-wrap-box {
width: 100% !important;
height: 100% !important;
position: relative;
}
.cropper-canvas {
width: 100% !important;
height: 100% !important;
position: relative;
}
.cropper-view-box {
border: 1px solid #fff;
box-shadow: 0 0 0 1px #39f;
outline: 0;
}
.cropper-face {
background-color: rgba(39, 90, 255, 0.1);
}
.cropper-line {
background-color: #39f;
}
.cropper-point {
background-color: #39f;
border: 1px solid #fff;
width: 8px;
height: 8px;
}
.cropper-center {
background-color: #39f;
border: 1px solid #fff;
width: 8px;
height: 8px;
}
.cropper-dashed {
border: 0 dashed #fff;
}
.cropper-modal {
background-color: rgba(0, 0, 0, 0.5);
}
.cropper-bg {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwgAADsIBFShKgAAAABl0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC4xOdTWsmQAAAGiSURBVDhPY2AYBYMRMDIyMjAyMjL8//+f4f///wz//v1j+Pv3L8OfP38Yfv/+zfDnzx+G37//MPz69Yvh58+fDD9+/GD4/v07w7dv3xi+fv3K8OXLF4bPnz8zfPr0ieHjx48MHz58YHj//j3Du3fvGN6+fcvw5s0bhtevXzO8evWK4eXLlwwvXrxgeP78OcOzZ88Ynj59yvDkyROGx48fMzx69Ijh4cOHDA8ePGC4f/8+w7179xju3r3LcOfOHYbbt28z3Lp1i+HmzZsMN27cYLh+/TrDtWvXGK5evcpw5coVhsuXLzNcunSJ4eLFiwwXLlxgOH/+PMO5c+cYzp49y3DmzBmG06dPM5w6dYrh5MmTDCdOnGA4fvw4w7FjxxiOHj3KcOTIEYbDhw8zHDp0iOHgwYMMBw4cYNi/fz/Dvn37GPbu3cuwZ88eht27dzPs2rWLYefOnQw7duxg2L59O8O2bdsYtm7dyrBlyxaGzZs3M2zatIlh48aNDBs2bGBYv349w7p16xjWrl3LsGbNGoZVq1YxrFy5kmHFihUMy5cvZ1i2bBnD0qVLGZYsWcKwePFihkWLFjEsXLiQYcGCBQzz59nmDdvHsPcuXMZ5syZwzB79myGWbNmMcycOZNhxowZDNOnT2eYNm0aw9SpUxmmTJnCMHnyZIZJkyYxTJw4kWHChAkM48ePZxg3bhzD2LFjGcaMGcMwevRohlGjRjGMHDmSYcSIEQzDhw9nGDZsGMPQoUMZhgwZwjB48GCGQYMG0Q8AABKxLhX5TqgFAAAAAElFTkSuQmCC');
}
}
</style>

View File

@ -0,0 +1,564 @@
<template>
<div class="form-container">
<el-form :model="form" :rules="rules" ref="form" label-width="100px">
<el-form-item label="商品图片" prop="image">
<div class="upload-area" @click="!selectedImage && triggerFileInput()">
<input
ref="fileInput"
type="file"
accept="image/*"
@change="handleFileChange"
style="display: none;"
>
<div v-if="!selectedImage" class="upload-placeholder">
<i class="el-icon-plus" style="font-size: 40px;"></i>
<!-- <p class="upload-tip">支持 JPGPNG 格式最大 5MB</p> -->
</div>
<div v-else class="image-preview">
<img :src="selectedImage" alt="预览图" @click="previewImage">
<div class="preview-overlay">
<i class="el-icon-zoom-in"></i>
</div>
</div>
</div>
<!-- 图片操作按钮 -->
<div v-if="selectedImage" class="image-actions">
<el-button size="mini" type="primary" @click="previewImage">预览图片</el-button>
<el-button size="mini" type="primary" @click="openCropper">裁剪图片</el-button>
<el-button size="mini" type="danger" @click="removeImage">删除</el-button>
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</el-form>
<!-- 图片裁剪弹窗 -->
<el-dialog
title="图片裁剪"
:visible.sync="showCropper"
width="60%"
top="5vh"
:before-close="closeCropper"
custom-class="cropper-dialog"
:append-to-body="true"
:modal-append-to-body="true"
:z-index="9999"
:destroy-on-close="true"
>
<div class="cropper-container">
<vue-cropper
ref="cropper"
:img="cropperImage"
:outputSize="1"
:info="true"
:full="true"
:canMove="true"
:canMoveBox="true"
:original="false"
:autoCrop="true"
:autoCropWidth="300"
:autoCropHeight="200"
:fixed="false"
:centerBox="true"
:infoTrue="true"
:high="true"
mode="cover"
:maxImgSize="2000"
:enlargeImg="1"
:limitMinSize="[100, 100]"
@realTime="realTime"
/>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="closeCropper">取消</el-button>
<el-button type="primary" @click="cropImage">确认裁剪</el-button>
</div>
</el-dialog>
<!-- 图片预览弹窗 -->
<el-dialog
title="图片预览"
:visible.sync="showPreview"
width="50%"
center
top="5vh"
:append-to-body="true"
:modal-append-to-body="true"
:z-index="9998"
:destroy-on-close="true"
custom-class="preview-dialog"
>
<div class="preview-container">
<img :src="selectedImage" alt="预览图" class="preview-image">
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="showPreview = false">关闭</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { VueCropper } from 'vue-cropper'
export default {
name: 'sendProduct',
components: {
VueCropper
},
data() {
return {
selectedImage: null,
showCropper: false,
showPreview: false,
cropperImage: '',
form: {
image: ''
},
rules: {
image: [
{ required: true, message: '请上传商品图片', trigger: 'change' }
]
}
}
},
methods: {
//
triggerFileInput() {
this.$refs.fileInput.click()
},
//
handleFileChange(event) {
const file = event.target.files[0]
if (file) {
//
if (file.size > 5 * 1024 * 1024) {
this.$message.error('图片大小不能超过5MB')
return
}
//
if (!file.type.startsWith('image/')) {
this.$message.error('请选择图片文件')
return
}
const reader = new FileReader()
reader.onload = (e) => {
this.selectedImage = e.target.result
this.form.image = e.target.result
this.$refs.form.validateField('image')
}
reader.readAsDataURL(file)
}
},
//
openCropper() {
if (this.selectedImage) {
this.cropperImage = this.selectedImage
this.showCropper = true
// DOM
this.$nextTick(() => {
if (this.$refs.cropper) {
this.$refs.cropper.refresh()
}
// dialog
setTimeout(() => {
const dialogWrapper = document.querySelector('.cropper-dialog')
if (dialogWrapper) {
dialogWrapper.style.zIndex = '9999'
}
}, 100)
})
}
},
//
closeCropper() {
this.showCropper = false
this.cropperImage = ''
},
//
realTime(data) {
//
console.log('裁剪实时数据:', data)
},
//
cropImage() {
this.$refs.cropper.getCropData((data) => {
// data base64
this.selectedImage = data
this.form.image = data
this.$message.success('图片裁剪成功!')
this.closeCropper()
})
},
//
removeImage() {
this.selectedImage = null
this.form.image = ''
this.$refs.fileInput.value = ''
this.$refs.form.validateField('image')
},
//
previewImage() {
if (this.selectedImage) {
this.showPreview = true
}
},
//
submitForm() {
this.$refs.form.validate((valid) => {
if (valid) {
console.log('表单数据:', this.form)
this.$message.success('提交成功!')
} else {
this.$message.error('请完善表单信息')
}
})
},
//
resetForm() {
this.$refs.form.resetFields()
this.selectedImage = null
this.$refs.fileInput.value = ''
}
}
}
</script>
<style scoped lang="scss">
.form-container {
padding: 20px;
max-width: 600px;
margin: 0 auto;
.upload-area {
display: flex;
align-items: center;
justify-content: flex-start;
overflow: hidden;
width: 80px;
height: 80px;
border: 2px dashed #ddd;
border-radius: 8px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
background: #fafafa;
display: flex;
align-items: center;
justify-content: center;
&:hover {
border-color: #409EFF;
background: #f0f9ff;
}
.upload-placeholder {
width: 100%;
height: 100%;
border: 1px solid #ddd;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
i {
color: #999;
display: block;
}
p {
margin: 5px 0;
color: #666;
&.upload-tip {
font-size: 12px;
color: #999;
}
}
}
.image-preview {
position: relative;
cursor: pointer;
img {
max-width: 100%;
max-height: 300px;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
transform: scale(1.05);
}
}
.preview-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
border-radius: 8px;
i {
color: white;
font-size: 24px;
}
}
&:hover .preview-overlay {
opacity: 1;
}
.image-actions {
margin-top: 15px;
display: flex;
gap: 10px;
justify-content: center;
}
}
.image-actions {
margin-top: 10px;
display: flex;
gap: 8px;
justify-content: center;
}
}
}
//
.cropper-dialog {
z-index: 9999 !important;
.el-dialog {
z-index: 9999 !important;
}
.el-dialog__wrapper {
z-index: 9999 !important;
}
.el-dialog__mask {
z-index: 9998 !important;
}
.cropper-container {
height: 350px;
display: flex;
justify-content: center;
align-items: center;
background: #f5f5f5;
border-radius: 8px;
padding: 20px;
}
.dialog-footer {
text-align: center;
padding: 20px 0;
}
}
//
.preview-dialog {
z-index: 9998 !important;
.el-dialog {
z-index: 9998 !important;
}
.el-dialog__wrapper {
z-index: 9998 !important;
}
.el-dialog__mask {
z-index: 9997 !important;
}
.preview-container {
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
background: #f5f5f5;
border-radius: 8px;
min-height: 300px;
.preview-image {
max-width: 100%;
max-height: 500px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
}
.dialog-footer {
text-align: center;
padding: 20px 0;
}
}
//
:global(.vue-cropper) {
.cropper-container {
width: 100% !important;
height: 100% !important;
}
.cropper-wrap-box {
width: 100% !important;
height: 100% !important;
}
.cropper-canvas {
width: 100% !important;
height: 100% !important;
}
.cropper-view-box {
border: 1px solid #fff;
box-shadow: 0 0 0 1px #39f;
}
.cropper-face {
background-color: rgba(39, 90, 255, 0.1);
}
.cropper-line {
background-color: #39f;
}
.cropper-point {
background-color: #39f;
border: 1px solid #fff;
width: 8px;
height: 8px;
}
}
// dialog
:global(.el-dialog__wrapper) {
&.cropper-dialog {
z-index: 9999 !important;
}
}
:global(.el-dialog) {
&.cropper-dialog {
z-index: 9999 !important;
}
}
// dialog
:global(.cropper-dialog) {
z-index: 9999 !important;
.el-dialog {
z-index: 9999 !important;
}
.el-dialog__wrapper {
z-index: 9999 !important;
}
.el-dialog__mask {
z-index: 9998 !important;
}
}
//
:deep(.vue-cropper) {
.cropper-view-box {
border-radius: 0;
}
.cropper-face {
background-color: rgba(39, 90, 255, 0.1);
}
}
//
:deep(.vue-cropper) {
width: 100% !important;
height: 100% !important;
position: relative;
.cropper-container {
width: 100% !important;
height: 100% !important;
position: relative;
}
.cropper-wrap-box {
width: 100% !important;
height: 100% !important;
position: relative;
}
.cropper-canvas {
width: 100% !important;
height: 100% !important;
position: relative;
}
.cropper-view-box {
border: 1px solid #fff;
box-shadow: 0 0 0 1px #39f;
outline: 0;
}
.cropper-face {
background-color: rgba(39, 90, 255, 0.1);
}
.cropper-line {
background-color: #39f;
}
.cropper-point {
background-color: #39f;
border: 1px solid #fff;
width: 8px;
height: 8px;
}
.cropper-center {
background-color: #39f;
border: 1px solid #fff;
width: 8px;
height: 8px;
}
.cropper-dashed {
border: 0 dashed #fff;
}
.cropper-modal {
background-color: rgba(0, 0, 0, 0.5);
}
.cropper-bg {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwgAADsIBFShKgAAAABl0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC4xOdTWsmQAAAGiSURBVDhPY2AYBYMRMDIyMjAyMjL8//+f4f///wz//v1j+Pv3L8OfP38Yfv/+zfDnzx+G37//MPz69Yvh58+fDD9+/GD4/v07w7dv3xi+fv3K8OXLF4bPnz8zfPr0ieHjx48MHz58YHj//j3Du3fvGN6+fcvw5s0bhtevXzO8evWK4eXLlwwvXrxgeP78OcOzZ88Ynj59yvDkyROGx48fMzx69Ijh4cOHDA8ePGC4f/8+w7179xju3r3LcOfOHYbbt28z3Lp1i+HmzZsMN27cYLh+/TrDtWvXGK5evcpw5coVhsuXLzNcunSJ4eLFiwwXLlxgOH/+PMO5c+cYzp49y3DmzBmG06dPM5w6dYrh5MmTDCdOnGA4fvw4w7FjxxiOHj3KcOTIEYbDhw8zHDp0iOHgwYMMBw4cYNi/fz/Dvn37GPbu3cuwZ88eht27dzPs2rWLYefOnQw7duxg2L59O8O2bdsYtm7dyrBlyxaGzZs3M2zatIlh48aNDBs2bGBYv349w7p16xjWrl3LsGbNGoZVq1YxrFy5kmHFihUMy5cvZ1i2bBnD0qVLGZYsWcKwePFihkWLFjEsXLiQYcGCBQzz59nmDdvHsPcuXMZ5syZwzB79myGWbNmMcycOZNhxowZDNOnT2eYNm0aw9SpUxmmTJnCMHnyZIZJkyYxTJw4kWHChAkM48ePZxg3bhzD2LFjGcaMGcMwevRohlGjRjGMHDmSYcSIEQzDhw9nGDZsGMPQoUMZhgwZwjB48GCGQYMG0Q8AABKxLhX5TqgFAAAAAElFTkSuQmCC');
}
}
</style>

View File

@ -0,0 +1,226 @@
<template>
<div class="main-box">
<div style="width: 1400px;border: 1px solid red;">
<div class="category-filter">
<!-- 所属类别 -->
<div class="category-section">
<div class="category-title">所属类别</div>
<div class="category-tags">
<span v-for="category in categories" :key="category.id"
:class="['category-tag', { active: selectedCategory === category.id }]"
@click="selectCategory(category.id)">
{{ category.name }}
</span>
</div>
</div>
<!-- 公司类别 -->
<div class="category-section">
<div class="category-title">公司类别</div>
<div class="category-tags">
<span v-for="company in companies" :key="company.id"
:class="['category-tag', { active: selectedCompany === company.id }]"
@click="selectCompany(company.id)">
{{ company.name }}
</span>
</div>
</div>
<!-- 产品 -->
<div class="product-card-container">
<productCard v-if="productList.length > 0" :productList="productList"></productCard>
<div v-else class="no-data">暂无数据</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'supplyAndDemandSquare',
components: {
productCard: () => import('@/views/homePage/ncmatch/mainPage/productCard/index.vue')
},
data() {
return {
selectedCategory: 'category2', //
selectedCompany: 'company1', //
categories: [
{ id: 'category1', name: '云' },
{ id: 'category2', name: '国产算力' },
{ id: 'category3', name: 'NVIDIA' },
{ id: 'category4', name: '网络服务' },
{ id: 'category5', name: '一体机' },
{ id: 'category6', name: '硬件' },
{ id: 'category7', name: 'AI应用' },
{ id: 'category8', name: '其他' }
],
companies: [
{ id: 'company1', name: '龙头企业' },
{ id: 'company2', name: '骨干企业' },
{ id: 'company3', name: '科技型企业' },
{ id: 'company4', name: '专精特新企业' },
{ id: 'company5', name: '高新技术企业' },
{ id: 'company6', name: '科技小巨人领军企业' },
{ id: 'company7', name: '其他' }
],
productList: []
}
},
methods: {
selectCategory(categoryId) {
this.selectedCategory = categoryId
},
selectCompany(companyId) {
this.selectedCompany = companyId
},
getProductList() {
this.productList = [
{
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-A100',
image: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE1MCIgdmlld0JveD0iMCAwIDIwMCAxNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTUwIiBmaWxsPSIjMjIyMjIyIi8+Cjx0ZXh0IHg9IjEwMCIgeT0iNzUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxNCIgZmlsbD0id2hpdGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiPk5WSUQpQSBSVFggNDA5MDwvdGV4dD4KPC9zdmc+Cg==',
price: 8000,
cpu: 'AMD EPYC 7542 32 C * 2',
memory: '128G DDR4-3200 * 8',
gpu: 'NVIDIA-A100-80GB * 8',
sys_disk: '1.92T SATA SSD * 2 (Raid)',
data_disk: '7.68T U.2 NVMe SSD * 1',
net_card: 'Mellanox Connect4 25G SFP28 2-port * 1'
},
{
id: 3,
name: '昇腾910B',
image: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE1MCIgdmlld0JveD0iMCAwIDIwMCAxNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTUwIiBmaWxsPSIjMjIyMjIyIi8+Cjx0ZXh0IHg9IjEwMCIgeT0iNzUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxNCIgZmlsbD0id2hpdGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiPk5WSUQpQSBSVFggNDA5MDwvdGV4dD4KPC9zdmc+Cg==',
price: 5000,
cpu: 'Kunpeng 920 64 C * 2',
memory: '64G DDR4-3200 * 8',
gpu: '昇腾910B-32GB * 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: 4,
name: '昆仑芯P800',
image: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE1MCIgdmlld0JveD0iMCAwIDIwMCAxNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTUwIiBmaWxsPSIjMjIyMjIyIi8+Cjx0ZXh0IHg9IjEwMCIgeT0iNzUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxNCIgZmlsbD0id2hpdGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiPk5WSUQpQSBSVFggNDA5MDwvdGV4dD4KPC9zdmc+Cg==',
price: 4000,
cpu: 'Kunpeng 920 64 C * 2',
memory: '64G DDR4-3200 * 8',
gpu: '昆仑芯P800-16GB * 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'
}
]
}
},
mounted() {
this.getProductList()
}
}
</script>
<style scoped lang="scss">
.product-list{
width: 100%;
border: 1px solid red;
// display: grid;
// grid-template-columns: repeat(4, 1fr);
// gap: 20px;
.no-data{
text-align: center;
font-size: 16px;
}
}
/* 产品卡片容器样式 */
.product-card-container {
margin-top: 20px;
width: 100%;
}
.main-box {
width: 100%;
border: 1px solid red;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
}
.category-filter {
// padding: 20px;
bottom: 1rem;
background-color: #fff;
border-radius: 8px;
margin: 20px;
margin-left: 0;
margin-right: 0;
.category-section {
display: flex;
justify-content: flex-start;
align-items: center;
margin-bottom: 20px;
border: 1px solid red;
&:last-child {
margin-bottom: 0;
}
.category-title {
font-size: 14px;
color: #333;
// margin-bottom: 12px;
font-weight: 500;
display: flex;
justify-content: flex-end;
align-items: center;
border: 1px solid red;
height: 100%;
margin-right: 15px;
}
.category-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
.category-tag {
padding: 3px 6px;
background-color: #fff;
border: 1px solid #e8e8e8;
border-radius: 2px;
font-size: 12px;
color: #666;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
border-color: #1890ff;
color: #1890ff;
}
&.active {
background-color: #e6f7ff;
border-color: #1890ff;
color: #1890ff;
}
}
}
}
}
</style>

View File

@ -1,6 +1,6 @@
export function getHomePath() { export function getHomePath() {
// let homePath= "/homePage/index" let homePath= "/homePage/index"
let homePath= "/ncmatchHome/index" // let homePath= "/ncmatchHome/index"
// 业主机构信息 // 业主机构信息
let url_link = window.location.href || ''; let url_link = window.location.href || '';