This commit is contained in:
木瓜一块八 2025-08-14 14:59:34 +08:00
parent 9bbaf61467
commit 285c9409d5
19 changed files with 1838 additions and 219 deletions

View File

@ -84,8 +84,35 @@ export function reqSearchByMangement(data){
return request({ return request({
url: '/product/publish_product_search.dspy', url: '/product/publish_product_search.dspy',
method: 'post', method: 'post',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/js1on' },
data data
}) })
} }
//编辑商品
export function reqEditProduct(data){
return request({
url: '/product/publish_product_update.dspy',
method: 'post',
headers: { 'Content-Type': 'multipart/form-data' },
data
})
}
//编辑非图片字段
export function reqEditProductNoImg(data){
return request({
url: '/product/publish_product_update.dspy',
method: 'post',
headers: { 'Content-Type': 'application/js1on' },
data
})
}
//搜索
export function reqSearch(data){
return request({
url: '/product/global_search.dspy',
method: 'post',
headers: { 'Content-Type': 'application/js1on' },
data
})
}

View File

@ -65,24 +65,6 @@ export const constantRoutes = [
component: () => import('@/views/beforeLogin/index.vue'), component: () => import('@/views/beforeLogin/index.vue'),
hidden: true hidden: true
}, },
{
alwaysShow: true,
path: "/productMangement",
component:Layout,
name: "productMangement",
redirect: "/productMangement/index",
meta: { fullPath: "/productMangement", title: "商品管理", noCache: true, icon: 'el-icon-s-home' },
children: [
{
path: "index",
component: () => import("@/views/customer/productMangement/productList/index.vue"),
name: "productList",
meta: { title: "商品清单", fullPath: "/productMangement/index" },
},
]
},
{ {
path: '/wxPage', path: '/wxPage',
@ -182,6 +164,7 @@ export const constantRoutes = [
meta: { title: "注册" }, meta: { title: "注册" },
}, },
{ {
hidden: true,
path: "/ncmatchHome", path: "/ncmatchHome",
component: () => import("@/views/homePage/ncmatch/index.vue"), component: () => import("@/views/homePage/ncmatch/index.vue"),
name: "ncmatchHome", name: "ncmatchHome",
@ -202,6 +185,13 @@ export const constantRoutes = [
hidden: true, hidden: true,
meta: { title: "算力供需广场", fullPath: "/ncmatch/supplyAndDemandSquare" }, meta: { title: "算力供需广场", fullPath: "/ncmatch/supplyAndDemandSquare" },
}, },
{
path: "search",
component: () => import("@/views/homePage/ncmatch/searchBox/index.vue"),
name: "search",
hidden: true,
meta: { title: "产品查询", fullPath: "/ncmatch/searchBox" },
},
] ]
}, },
{ {
@ -311,6 +301,85 @@ export const asyncRoutes = [
// name: 'productHome', // name: 'productHome',
// meta: {title: "概览", fullPath: "/productHome", noCache: true} // meta: {title: "概览", fullPath: "/productHome", noCache: true}
// }, // },
{
path: "/approveMangement",
component:Layout,
name: "approveMangement",
redirect: "/approveMangement/index",
meta: { fullPath: "/approveMangement", title: "审核管理", noCache: true, icon: 'el-icon-s-home' },
children: [
{
path: "pendingPro",
component: () => import("@/views/customer/approveMangement/pendingPro.vue"),
name: "pendingPro",
meta: { title: "待审清单", fullPath: "/approveMangement/index" },
},
{
path: "approvedPro",
component: () => import("@/views/customer/approveMangement/approvedPro.vue"),
name: "approvedPro",
meta: { title: "已审清单", fullPath: "/approveMangement/approvedPro" },
},
]
},
{
path: "/productMangement",
component:Layout,
name: "productMangement",
redirect: "/productMangement/index",
meta: { fullPath: "/productMangement", title: "商品管理", noCache: true, icon: 'el-icon-s-home' },
children: [
{
path: "index",
component: () => import("@/views/customer/productMangement/productList/index.vue"),
name: "productList",
meta: { title: "商品发布清单", fullPath: "/productMangement/index" },
},
{
path: "rejectedPro",
component: () => import("@/views/customer/productMangement/rejectedPro/index.vue"),
name: "rejectedPro",
meta: { title: "商品未通过清单", fullPath: "/productMangement/index" },
},
]
},
{
path: "/menuMangement",
component:Layout,
name: "menuMangement",
redirect: "/menuMangement/index",
meta: { fullPath: "/menuMangement", title: "菜单管理", noCache: true, icon: 'el-icon-s-home' },
children: [
{
path: "index",
component: () => import("@/views/operation/menuMangement/index.vue"),
name: "menuMangement",
meta: { title: "菜单管理", fullPath: "/menuMangement/index" },
},
]
},
{
path: "/demandMangement",
component:Layout,
name: "demandMangement",
redirect: "/demandMangement/index",
meta: { fullPath: "/demandMangement", title: "需求管理", noCache: true, icon: 'el-icon-s-home' },
children: [
{
path: "index",
component: () => import("@/views/customer/demand/demandList.vue"),
name: "demandList",
meta: { title: "需求发布清单", fullPath: "/demandMangement/index" },
},
{
path: "rejectDemand",
component: () => import("@/views/customer/demand/rejectDemand.vue"),
name: "rejectDemand",
meta: { title: "需求未通过清单", fullPath: "/demandMangement/rejectDemand" },
},
]
},
{ {
path: getHomePath() == '/ncmatchHome/index' ? "/ncmatchHome" : "/homePage", path: getHomePath() == '/ncmatchHome/index' ? "/ncmatchHome" : "/homePage",

View File

@ -0,0 +1,6 @@
import Vue from 'vue'
// 创建事件总线实例
const eventBus = new Vue()
export default eventBus

View File

@ -0,0 +1,26 @@
<template>
<div>
<commonBox :role="role"></commonBox>
</div>
</template>
<script>
//| |
import commonBox from '@/views/customer/productMangement/commonBox/index.vue';
export default {
name: 'productList',
components: { commonBox },
data() {
return {
role:{
role_type:'customer',
audit_status:'approved'
}
}
},
created() {
},
}
</script>

View File

@ -0,0 +1,26 @@
<template>
<div>
<commonBox :role="role"></commonBox>
</div>
</template>
<script>
//| |
import commonBox from '@/views/customer/productMangement/commonBox/index.vue';
export default {
name: 'productList',
components: { commonBox },
data() {
return {
role:{
role_type:'customer',
audit_status:'pending'
}
}
},
created() {
},
}
</script>

View File

@ -0,0 +1,36 @@
<template>
<div>
<commonBox :role="role"></commonBox>
</div>
</template>
<script>
//| |
import commonBox from '@/views/customer/productMangement/commonBox/index.vue';
export default {
name: 'productList',
components: { commonBox },
data() {
return {
role:{
role_type:'customer',
audit_status:'',
publish_type:null
}
}
},
created() {
if(sessionStorage.getItem('jueseNew').includes('运营')){
console.log("运营");
this.role.role_type='customer'
}else if(sessionStorage.getItem('jueseNew').includes('客户')){
console.log("客户");
this.role.role_type='user'
}
console.log("role",this.role);
this.role.audit_status='pending,approved'
this.role.publish_type='2'
},
}
</script>

View File

@ -0,0 +1,36 @@
<template>
<div>
<commonBox :role="role"></commonBox>
</div>
</template>
<script>
//| |
import commonBox from '@/views/customer/productMangement/commonBox/index.vue';
export default {
name: 'productList',
components: { commonBox },
data() {
return {
role:{
role_type:'customer',
audit_status:'',
publish_type:null
}
}
},
created() {
if(sessionStorage.getItem('jueseNew').includes('运营')){
console.log("运营");
this.role.role_type='customer'
}else if(sessionStorage.getItem('jueseNew').includes('客户')){
console.log("客户");
this.role.role_type='user'
}
console.log("role",this.role);
this.role.audit_status='rejected'
this.role.publish_type='2'
},
}
</script>

View File

@ -12,7 +12,7 @@
</li> </li>
<li> <li>
<el-input size="mini" v-model="searchData.keyword" placeholder="请输入商品名称或关键词" clearable></el-input> <el-input size="mini" v-model="searchData.keyword" placeholder="请输入关键词" clearable></el-input>
</li> </li>
<li> <li>
<el-button size="mini" @click="resetSearch">重置</el-button> <el-button size="mini" @click="resetSearch">重置</el-button>
@ -22,17 +22,21 @@
</div> </div>
<div class="table-box"> <div class="table-box">
<div style="margin-bottom: 10px;"> <div style="margin-bottom: 10px;">
<el-radio-group v-model="searchData.radioType" class="radio-group" size="mini" <el-radio-group v-if="role.role_type=='customer'" v-model="searchData.radioType" class="radio-group" size="mini"
@change="handleRadioChange"> @change="handleRadioChange">
<el-radio-button v-for="item in radioOptions" :key="item.value" :label="item.value"> <el-radio-button v-for="item in radioOptions" :key="item.value" :label="item.value">
{{ item.label }} {{ item.label }}
</el-radio-button> </el-radio-button>
</el-radio-group> </el-radio-group>
<!-- <el-button style="margin-left: 10px;" size="mini" @click="openAddDialog">
<i class="el-icon-plus"></i> 添加{{ searchData.radioType === '1' || searchData.radioType === '3' ?
'需求' : '商品' }}
</el-button> -->
<el-button style="margin-left: 10px;" size="mini" @click="exportData"> <el-button style="margin-left: 10px;" size="mini" @click="exportData">
<i class="el-icon-upload2"></i> 导出 <i class="el-icon-upload2"></i> 导出
</el-button> </el-button>
</div> </div>
<el-table height="calc(100vh - 210px)" v-loading="loading" :data="tableData" <el-table border height="calc(100vh - 210px)" v-loading="loading" :data="tableData"
style="width: 100%;border:1px solid #ccc;" element-loading-text="加载中..." style="width: 100%;border:1px solid #ccc;" element-loading-text="加载中..."
element-loading-spinner="el-icon-loading" element-loading-background="rgba(255, 255, 255, 0.8)"> element-loading-spinner="el-icon-loading" element-loading-background="rgba(255, 255, 255, 0.8)">
<el-table-column prop="date" label="商品图片" width="180"> <el-table-column prop="date" label="商品图片" width="180">
@ -49,14 +53,29 @@
</el-table-column> </el-table-column>
<el-table-column prop="product_category" show-overflow-tooltip label="所属类别" min-width="180"> <el-table-column prop="product_category" show-overflow-tooltip label="所属类别" min-width="180">
</el-table-column> </el-table-column>
<el-table-column show-overflow-tooltip label="审核状态" min-width="180">
<template slot-scope="scope">
<el-tag v-if="scope.row.audit_status === 'approved'" type="success">审核通过</el-tag>
<el-tag v-else-if="scope.row.audit_status === 'rejected'" type="danger">审核不通过</el-tag>
<el-tag v-else type="warning">待审核</el-tag>
</template>
</el-table-column>
<el-table-column show-overflow-tooltip label="上架状态" min-width="180">
<template slot-scope="scope">
<el-tag v-if="scope.row.listing_status === 'listing'" type="success">已上架</el-tag>
<el-tag v-else-if="scope.row.listing_status === 'delisting'" type="danger">已下架</el-tag>
<el-tag v-else type="warning">未上架</el-tag>
</template>
</el-table-column>
<el-table-column prop="company_name" show-overflow-tooltip label="所属企业" min-width="180"> <el-table-column prop="company_name" show-overflow-tooltip label="所属企业" min-width="180">
</el-table-column> </el-table-column>
<el-table-column prop="company_type" show-overflow-tooltip label="公司类别" min-width="180"> <el-table-column prop="company_type" show-overflow-tooltip label="公司类别" min-width="180">
</el-table-column> </el-table-column>
<el-table-column prop="address" show-overflow-tooltip label="商品概述" min-width="180"> <!-- <el-table-column prop="requirement_summary" show-overflow-tooltip label="商品概述" min-width="180">
</el-table-column>
<el-table-column prop="address" show-overflow-tooltip label="商品详情" min-width="180">
</el-table-column> </el-table-column>
<el-table-column prop="related_parameters" show-overflow-tooltip label="相关参数" min-width="180">
</el-table-column> -->
<el-table-column prop="contact_person" show-overflow-tooltip label="联系人" min-width="180"> <el-table-column prop="contact_person" show-overflow-tooltip label="联系人" min-width="180">
</el-table-column> </el-table-column>
<el-table-column prop="phone_number" show-overflow-tooltip label="联系电话" min-width="180"> <el-table-column prop="phone_number" show-overflow-tooltip label="联系电话" min-width="180">
@ -65,12 +84,19 @@
</el-table-column> </el-table-column>
<el-table-column fixed="right" min-width="240" label="操作"> <el-table-column fixed="right" min-width="240" label="操作">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="text" size="small" @click="openDetail(scope.row)">查看</el-button> <el-button type="text" size="small" @click="openDetail(scope.row,1)">查看</el-button>
<el-button type="text" style="color: #409EFF;" size="small">修改</el-button> <el-button @click="editProduct(scope.row)" type="text" style="color: #409EFF;"
size="small">修改</el-button>
<el-button v-if="role.role_type=='customer'" type="text" size="small" @click="openDetail(scope.row,2)">审核</el-button>
<!-- <el-button type="text" style="color: #E6A23C;" size="small">导出</el-button> --> <!-- <el-button type="text" style="color: #E6A23C;" size="small">导出</el-button> -->
<el-button type="text" style="color: #67C23A;" size="small">上架</el-button> <el-button :disabled="scope.row.audit_status === 'approved'&&scope.row.listing_status === 'listing'" v-if="role.role_type=='customer'" @click="grounding(scope.row, 'listing')" type="text" style="color: #67C23A;"
<el-button type="text" size="small">下架</el-button> size="small">上架</el-button>
<el-button type="text" style="color: #F56C6C;" size="small">删除</el-button> <el-button :disabled="scope.row.audit_status === 'approved'&&scope.row.listing_status === 'delisting'" v-if="role.role_type=='customer'" @click="grounding(scope.row, 'delisting')" type="text" size="small">下架</el-button>
<el-popconfirm v-if="role.role_type=='customer'" title="确定删除该条目吗?" @confirm="deleteProduct(scope.row)">
<el-button slot="reference" type="text" style="color: #F56C6C;margin-left: 10px;" size="small">删除</el-button>
</el-popconfirm>
</template> </template>
</el-table-column> </el-table-column>
@ -80,21 +106,35 @@
layout="total, prev, pager, next" :total="total_count"> layout="total, prev, pager, next" :total="total_count">
</el-pagination> </el-pagination>
</div> </div>
<commonDetail <commonDetail :showType="detailType" :visible="showProductDetail" @close="handleDetailClose" @approve="handleApprove"
:visible="showProductDetail"
@close="handleDetailClose"
@approve="handleApprove"
@reject="handleReject"> @reject="handleReject">
</commonDetail> </commonDetail>
<!-- 编辑商品弹窗 -->
<el-dialog :title="editForm.publish_type === '1' ? '编辑商品' : '编辑需求'" :visible.sync="showEditDialog" width="80%"
top="5vh">
<sendProduct :publish_type="editForm.publish_type" :isEdit="true" :editData="editForm"
@success="handleEditSuccess" @close="closeEditDialog">
</sendProduct>
</el-dialog>
<!-- 添加商品/需求弹窗 -->
<el-dialog :title="publish_type === '2' ? '添加需求' : '添加商品'" width="80%" top="5vh"
:visible.sync="sendProductVisible">
<sendProduct :publish_type="publish_type" :isEdit="false" @success="sendProductSuccess"
@close="closeAddDialog">
</sendProduct>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
import { reqSearchByMangement, reqGetProductDetail } from '@/api/ncmatch'; import { reqSearchByMangement, reqGetProductDetail, reqEditProduct, reqEditProductNoImg } from '@/api/ncmatch';
import commonDetail from '@/views/customer/productMangement/commonDetail/index.vue'; import commonDetail from '@/views/customer/productMangement/commonDetail/index.vue';
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import sendProduct from '@/views/homePage/ncmatch/mainPage/sendProduct/index.vue';
export default { export default {
name: 'commonBox', name: 'commonBox',
components: { commonDetail }, components: { commonDetail, sendProduct },
props: { props: {
role: { role: {
type: Object, type: Object,
@ -103,13 +143,15 @@ export default {
}, },
data() { data() {
return { return {
detailType: '1',//1: 2: 3:
sendProductVisible: false,
searchDate: [], searchDate: [],
searchData: { searchData: {
radioType: "1", radioType: "1",
start_date: null, start_date: null,
end_date: null, end_date: null,
keyword: "", keyword: "",
audit_status: null, audit_status: null, // this.role.audit_status
listing_status: null, listing_status: null,
publish_type: "2", publish_type: "2",
manager_self: "", manager_self: "",
@ -164,6 +206,8 @@ export default {
tableData: [], tableData: [],
total_count: 0, total_count: 0,
loading: false, loading: false,
showEditDialog: false,
editForm: {},
pickerOptions: { pickerOptions: {
shortcuts: [{ shortcuts: [{
text: '最近一周', text: '最近一周',
@ -194,9 +238,84 @@ export default {
} }
}, },
created() { created() {
console.log("父组件传来的值是",this.role);
// created audit_status
if (this.role && this.role.audit_status) {
this.searchData.audit_status = this.role.audit_status;
}
if (this.role && this.role.publish_type) {
this.searchData.publish_type = this.role.publish_type;
}
this.getTableData(); this.getTableData();
}, },
watch: {
// role
role: {
handler(newRole) {
if (newRole && newRole.audit_status) {
this.searchData.audit_status = newRole.audit_status;
}
},
immediate: true,
deep: true
}
},
computed: {
showProductDetail: {
get() {
return this.$store.state.ncmatch.showProductDetail;
},
set(value) {
this.$store.commit('SHOWPRODUCTDETAIL', value);
}
}
},
methods: { methods: {
deleteProduct(row) {
console.log(row);
let ploay = {
id: row.id,
del_flg: '1'
}
reqEditProductNoImg(ploay).then(res => {
if (res.status) {
this.$message.success('删除成功~');
this.getTableData();
} else {
this.$message.error('删除失败~');
}
})
},
grounding(row, listing_status) {
console.log(listing_status);
let ploay = {
id: row.id,
listing_status: listing_status,
}
reqEditProductNoImg(ploay).then(res => {
let msg = listing_status == 'listing' ? '上架' : '下架';
if (res.status) {
this.$message.success(msg + '成功~');
this.getTableData();
} else {
this.$message.error(msg + '失败~');
}
})
},
sendProductSuccess() {
this.sendProductVisible = false; //
this.getTableData(); //
},
//
closeAddDialog() {
this.sendProductVisible = false;
},
openSendProduct(item) {
this.sendProductVisible = true;
this.sendProductData = item;
},
handleRadioChange(value) { handleRadioChange(value) {
this.searchData.publish_type = this.radioMap[value].publish_type; this.searchData.publish_type = this.radioMap[value].publish_type;
this.searchData.manager_self = this.radioMap[value].manager_self; this.searchData.manager_self = this.radioMap[value].manager_self;
@ -208,6 +327,7 @@ export default {
this.searchDate = []; this.searchDate = [];
this.searchData.keyword = ""; this.searchData.keyword = "";
this.searchData.current_page = 1; this.searchData.current_page = 1;
this.searchData.audit_status = this.role.audit_status;
this.getTableData(); this.getTableData();
}, },
exportData() { exportData() {
@ -220,13 +340,35 @@ export default {
handleApprove() { handleApprove() {
// //
console.log('审核通过'); console.log('审核通过');
this.getTableData();
// //
}, },
handleReject() { handleReject() {
// //
console.log('审核不通过'); console.log('审核不通过');
this.getTableData();
// //
}, },
//
editProduct(item) {
console.log('编辑商品数据:', item);
console.log('公司类别数据:', item.company_type);
this.editForm = { ...item };
this.showEditDialog = true;
},
//
closeEditDialog() {
this.showEditDialog = false;
this.editForm = {};
},
//
handleEditSuccess() {
//
const successMessage = this.editForm.publish_type === '2' ? '需求编辑成功!' : '商品编辑成功!';
this.$message.success(successMessage);
this.closeEditDialog();
this.getTableData(); //
},
getTableData() { getTableData() {
// //
if (this.searchDate && this.searchDate.length === 2) { if (this.searchDate && this.searchDate.length === 2) {
@ -257,37 +399,33 @@ export default {
console.log(`每页 ${val}`); console.log(`每页 ${val}`);
}, },
handleCurrentChange(val) { handleCurrentChange(val) {
this.searchData.current_page = val;
this.getTableData(); this.getTableData();
console.log(`当前页: ${val}`); console.log(`当前页: ${val}`);
}, },
openDetail(item) { openDetail(item,type) {
this.detailType = type;
this.loading = true; this.loading = true;
reqGetProductDetail({ reqGetProductDetail({
id: item.id, id: item.id,
from: 'b' from: 'b'
}).then(async res => { }).then(async res => {
this.loading = false this.loading = false;
if (res.status) { if (res.status) {
await this.$store.commit('SETPRODUCTDETAIL', res.data); await this.$store.commit('SETPRODUCTDETAIL', res.data);
await this.$store.commit('SHOWPRODUCTDETAIL', true); await this.$store.commit('SHOWPRODUCTDETAIL', true);
} else { } else {
this.$message.error(res.msg); this.$message.error(res.msg || '获取详情失败');
this.$set(this.loadingStates, item.id, false);
} }
}).catch(error => {
this.loading = false;
}) console.error('获取详情失败:', error);
} this.$message.error('获取详情失败,请重试');
}, });
computed: { },
showProductDetail: { openAddDialog() {
get() { this.publish_type = this.searchData.radioType === '1' || this.searchData.radioType === '3' ? '2' : '1';
return this.$store.state.ncmatch.showProductDetail; this.sendProductVisible = true;
},
set(value) {
this.$store.commit('SHOWPRODUCTDETAIL', value);
}
} }
} }
} }
@ -322,7 +460,7 @@ export default {
.el-radio-button__inner { .el-radio-button__inner {
border-radius: 4px; border-radius: 4px;
margin-right: 8px; margin-right: 8px;
border: 1px solid #dcdfe6; border: 1px solid #dcdfe6;
background-color: #f5f7fa; background-color: #f5f7fa;
color: #606266; color: #606266;

View File

@ -1,23 +1,34 @@
<template> <template>
<el-dialog <div>
:title="dialogTitle" <el-dialog :title="dialogTitle" top="5vh" :visible.sync="visible" width="80%" @open="scrollToTop"
top="5vh" @close="handleClose">
:visible.sync="visible" <ProductDetail :isApprove="true" ></ProductDetail>
width="80%" <span slot="footer" class="dialog-footer">
@open="scrollToTop" <el-button @click="handleClose"> </el-button>
@close="handleClose"> <el-button type="success" v-if="showType===2" @click="handleApprove">审核通过</el-button>
<ProductDetail></ProductDetail> <el-button type="danger" v-if="showType===2" @click="handleReject">审核不通过</el-button>
<span slot="footer" class="dialog-footer"> </span>
<el-button @click="handleClose"> </el-button> </el-dialog>
<el-button type="success" @click="handleApprove">审核通过</el-button> <!-- Form -->
<el-button type="danger" @click="handleReject">审核不通过</el-button>
</span> <el-dialog title="审核退回" :visible.sync="dialogFormVisible">
</el-dialog> <el-form :model="form">
<el-form-item label="驳回原因" :label-width="formLabelWidth">
<el-input v-model="form.reject_reason" autocomplete="off" type="textarea" :rows="3"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false"> </el-button>
<el-button type="primary" @click="rejectProduct"> </el-button>
</div>
</el-dialog>
</div>
</template> </template>
<script> <script>
import ProductDetail from '@/views/homePage/ncmatch/proDetail/index.vue'; import ProductDetail from '@/views/homePage/ncmatch/proDetail/index.vue';
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import { reqEditProductNoImg } from '@/api/ncmatch';
export default { export default {
name: 'commonDetail', name: 'commonDetail',
@ -26,6 +37,19 @@ export default {
visible: { visible: {
type: Boolean, type: Boolean,
default: false default: false
},
showType: {
type: String,
default: '1'
}
},
data() {
return {
dialogFormVisible: false,
form: {
reject_reason: ''
},
formLabelWidth: '120px'
} }
}, },
computed: { computed: {
@ -56,26 +80,56 @@ export default {
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
// API // API
this.$emit('approve'); let ploay = {
this.$message.success('审核通过成功'); id: this.productDetailInfo.id,
this.handleClose(); audit_status: "approved"
}
reqEditProductNoImg(ploay).then(res => {
if (res.status) {
this.$emit('approve');
this.$message.success('审核通过成功');
this.handleClose();
} else {
this.$message.error(res.msg || '审核失败,请重试')
}
})
}).catch(() => { }).catch(() => {
this.$message.info('已取消操作'); this.$message.info('已取消操作');
}); });
}, },
handleReject() { handleReject() {
this.$confirm('确认审核不通过吗?', '提示', { this.dialogFormVisible = true;
confirmButtonText: '确定', // this.$confirm('', '', {
cancelButtonText: '取消', // confirmButtonText: '',
type: 'warning' // cancelButtonText: '',
}).then(() => { // type: 'warning'
// API // }).then(() => {
this.$emit('reject'); // // API
this.$message.success('审核不通过成功'); // this.$emit('reject');
this.handleClose(); // this.$message.success('');
}).catch(() => { // this.handleClose();
this.$message.info('已取消操作'); // }).catch(() => {
}); // this.$message.info('');
// });
},
rejectProduct() {
let ploay={
id:this.productDetailInfo.id,
audit_status:"rejected",
reject_reason:this.form.reject_reason
}
reqEditProductNoImg(ploay).then(res => {
this.dialogFormVisible=false;
if (res.status) {
this.$emit('reject');
this.handleClose();
this.$message.success('审核成功');
}else{
this.$message.error('审核失败,请重试');
}
})
} }
} }
} }

View File

@ -14,12 +14,22 @@ export default {
return { return {
role:{ role:{
role_type:'customer', role_type:'customer',
type:'look' audit_status:'',
publish_type:null
} }
} }
}, },
created() { created() {
if(sessionStorage.getItem('jueseNew').includes('运营')){
console.log("运营");
this.role.role_type='customer'
}else if(sessionStorage.getItem('jueseNew').includes('客户')){
console.log("客户");
this.role.role_type='user'
}
console.log("role",this.role);
this.role.audit_status='pending,approved'
this.role.publish_type='1'
}, },
} }

View File

@ -0,0 +1,37 @@
<template>
<div>
<commonBox :role="role"></commonBox>
</div>
</template>
<script>
//| |
import commonBox from '@/views/customer/productMangement/commonBox/index.vue';
export default {
name: 'productList',
components: { commonBox },
data() {
return {
role:{
role_type:null,
audit_status:null,
publish_type:null
}
}
},
created() {
if(sessionStorage.getItem('jueseNew').includes('运营')){
console.log("运营");
this.role.role_type='customer'
}else if(sessionStorage.getItem('jueseNew').includes('客户')){
console.log("客户");
this.role.role_type='user'
}
console.log("role",this.role);
this.role.audit_status='rejected'
this.role.publish_type='1'
},
}
</script>

View File

@ -181,20 +181,12 @@ export default Vue.extend({
<template> <template>
<div class="jd-homepage"> <div class="jd-homepage">
<!-- 顶部Header --> <!-- 顶部Header -->
<header class="header"> <!-- <header class="header">
<div class="header-content"> <div class="header-content">
<!-- 左侧Logo -->
<!-- <div class="logo-section"> -->
<!--
<div class="logo">
<img src="./img/logo.png" alt="">
</div> -->
<!-- </div> -->
</div> </div>
</header> </header> -->
<!-- 主内容区域 --> <!-- 主内容区域 -->
<main class="main-content"> <main class="main-content">
<div class="content-wrapper"> <div class="content-wrapper">
@ -435,7 +427,6 @@ export default Vue.extend({
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
position: relative; position: relative;
height: 100px;
.header-content { .header-content {
width: 100%; width: 100%;
@ -506,7 +497,7 @@ export default Vue.extend({
margin: 20px auto; margin: 20px auto;
margin-top: 10px; margin-top: 10px;
border-radius: 10px; border-radius: 10px;
overflow: hidden; overflow: visible;
background-color: white; background-color: white;
height: 360px; height: 360px;

View File

@ -1,12 +1,20 @@
export function buildDynamicStructure(data, parentId = "None", currentLevel = 1) { export function buildDynamicStructure(data, parentId = null, currentLevel = 1) {
// 1. 找出当前层级的节点 // 1. 找出当前层级的节点
const currentNodes = data.filter(item => item.parentid === parentId); const currentNodes = data.filter(item => item.parentid === parentId);
if (currentNodes.length === 0) return []; if (currentNodes.length === 0) return [];
// 2. 处理每个节点 // 2. 处理每个节点
return currentNodes.map(node => { return currentNodes.map((node, index) => {
const resultNode = {}; const resultNode = {};
// 为每个节点添加唯一id
if (node.id) {
resultNode.id = node.id;
} else {
// 如果没有id生成一个唯一的id
resultNode.id = `level${currentLevel}_${parentId || 'root'}_${index}_${Date.now()}`;
}
// 设置层级名称字段 // 设置层级名称字段
if (currentLevel === 1) { if (currentLevel === 1) {
resultNode.first_level_name = node.name; resultNode.first_level_name = node.name;
@ -15,6 +23,9 @@ export function buildDynamicStructure(data, parentId = "None", currentLevel = 1)
resultNode.second_level_name = node.name; resultNode.second_level_name = node.name;
} else if (currentLevel === 3) { } else if (currentLevel === 3) {
resultNode.third_level_name = node.name; resultNode.third_level_name = node.name;
} else if (currentLevel === 4) {
// 第四级节点也需要保存name字段
resultNode.name = node.name;
} }
// 3. 递归处理子节点 // 3. 递归处理子节点
@ -27,9 +38,12 @@ export function buildDynamicStructure(data, parentId = "None", currentLevel = 1)
} else if (currentLevel === 2) { } else if (currentLevel === 2) {
resultNode.thirdClassification = children; resultNode.thirdClassification = children;
} else if (currentLevel === 3) { } else if (currentLevel === 3) {
// 第四级特殊处理为product_list // 第四级特殊处理为product_list
resultNode.product_list = children.map(child => ({ resultNode.product_list = children.map(child => ({
first_level_name: child.third_level_name || child.name id: child.id,
first_level_name: child.name // 修复:使用 child.name 而不是 child.third_level_name
})); }));
} }
} }

View File

@ -4,14 +4,14 @@
<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" style="color: #E02020;"><img src="../img/hot.svg" style="margin-right: 10px;"
alt=""> 热门推荐 / 活动促销</li> alt=""> 热门推荐 / 活动促销</li>
<li v-for="category in categories" :key="category.first_level_name" 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> <span class="category-icon"> <img style="width: 24px;height: 24px;" :src="category.icon" alt=""> </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 style="display: flex;margin-left: 0px;padding-left: 0px;">|</span>
<div class="menu-item"> <div class="menu-item">
<span v-for="(secondary, index) in category.secondaryClassification" <span v-for="(secondary, index) in category.secondaryClassification"
:key="secondary.second_level_name"> :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>
@ -19,12 +19,12 @@
</ul> </ul>
<transition name="slide-fade"> <transition name="slide-fade">
<!-- v-if="currentCategory" --> <!-- v-if="currentCategory" -->
<div class="rightBox" v-if="currentCategory" @mouseenter="keepProductList" @mouseleave="hideProductList"> <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" @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" <div v-for="secondary in currentCategory.secondaryClassification"
:key="secondary.second_level_name" class="secondary-item" :key="secondary.id" class="secondary-item"
:class="{ active: selectedSecondary === secondary }" :class="{ active: selectedSecondary === secondary }"
@mouseenter="selectSecondary(secondary)"> @mouseenter="selectSecondary(secondary)">
{{ secondary.second_level_name }} {{ secondary.second_level_name }}
@ -36,7 +36,7 @@
<!-- 如果有选中的二级菜单且有三级菜单显示京东风格的分类区域 --> <!-- 如果有选中的二级菜单且有三级菜单显示京东风格的分类区域 -->
<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.third_level_name" <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">
@ -48,7 +48,7 @@
<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 v-for="(product, index) in third.product_list" <div v-for="(product, index) in third.product_list"
:key="product.first_level_name" class="product-tag"> :key="product.id" class="product-tag">
{{ product.first_level_name }} {{ product.first_level_name }}
</div> </div>
</div> </div>
@ -82,7 +82,7 @@
<!-- 默认显示所有二级菜单项 --> <!-- 默认显示所有二级菜单项 -->
<div v-else class="jd-style-menu"> <div v-else class="jd-style-menu">
<div v-for="secondary in currentCategory.secondaryClassification" <div v-for="secondary in currentCategory.secondaryClassification"
:key="secondary.second_level_name" class="category-section"> :key="secondary.id" 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>
@ -112,61 +112,8 @@ export default {
currentCategory: null, currentCategory: null,
selectedSecondary: null, selectedSecondary: null,
hideTimer: null, hideTimer: null,
categories: [ categories: [ ],
{ loading: false,
first_level_name: '云', icon: require('../img/cloud.png'), secondaryClassification: [
{
second_level_name: '百度云',
thirdClassification: [
{
third_level_name: '计算',
product_list: [{ first_level_name: 'ECS1' }, { first_level_name: 'ECS2' }, { first_level_name: 'ECS3' }]
},
{
third_level_name: '存储',
product_list: [{ first_level_name: 'BOS1' }, { first_level_name: 'BOS2' }]
}
]
},
{
second_level_name: '阿里云',
thirdClassification: [
{
third_level_name: '计算',
product_list: [{ first_level_name: 'ECS-A' }, { first_level_name: 'ECS-B' }]
}
]
}
]
},
{
first_level_name: '国产算力', icon: require('../img/cloud.png'), secondaryClassification: [
{
second_level_name: '昇腾910B',
thirdClassification: [
{
third_level_name: '昇腾910B1',
},
{
third_level_name: '昇腾910B2',
}
]
},
{
second_level_name: '昆仑芯',
thirdClassification: [
{
third_level_name: '昆仑芯A',
},
{
third_level_name: '昆仑芯B',
}
]
}
]
}
],
} }
}, },
created() { created() {
@ -174,9 +121,13 @@ export default {
}, },
methods: { methods: {
getCategories() { getCategories() {
this.loading = true;
reqNcMatchMenu({ url_link: window.location.href }).then(res => { reqNcMatchMenu({ url_link: window.location.href }).then(res => {
this.loading = false;
if (res.status) { if (res.status) {
this.categories = buildDynamicStructure(res.data) this.categories = buildDynamicStructure(res.data)
console.log("测试",this.categories);
// this.categories = res.data.map(item => { // this.categories = res.data.map(item => {
// return { // return {
// name: item.name, // name: item.name,
@ -236,6 +187,7 @@ export default {
// //
.category-sidebar { .category-sidebar {
position: relative;
background-color: #f8fbfe; background-color: #f8fbfe;
height: 100%; height: 100%;
border-radius: 10px; border-radius: 10px;
@ -306,14 +258,17 @@ export default {
position: absolute; position: absolute;
left: 100%; left: 100%;
top: 0; top: 0;
width: 900px; border: 1px solid red;
width: 1000px;
background: white; background: white;
border-radius: 8px; border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 1000; z-index: 9999;
padding: 25px; padding: 25px;
min-height: 400px; // height: 500px;
margin-left: 10px; margin-left: 10px;
overflow: visible;
white-space: nowrap;
/* 添加一个透明的连接区域,防止鼠标移动时意外消失 */ /* 添加一个透明的连接区域,防止鼠标移动时意外消失 */
&::before { &::before {
@ -327,7 +282,10 @@ export default {
} }
.rightBox-content { .rightBox-content {
.secondary-menu { .secondary-menu {
line-height: 1.5; line-height: 1.5;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@ -355,6 +313,10 @@ export default {
.menu-content { .menu-content {
min-height: 200px; min-height: 200px;
height: 100%;
max-height: calc(100vh - 280px);
overflow-y: auto;
border: 5px solid red;
.jd-style-menu { .jd-style-menu {
width: 100%; width: 100%;

View File

@ -1,5 +1,8 @@
<template> <template>
<div class="form-container"> <div class="form-container">
<div v-if="form.reject_reason" class="tip">
<i class="el-icon-error"></i>未通过原因: <span style="color: #666;font-size: 12px;">{{ form.reject_reason }}</span>
</div>
<el-form :model="form" :rules="rules" ref="form" label-width="120px" class="two-column-form"> <el-form :model="form" :rules="rules" ref="form" label-width="120px" class="two-column-form">
<!-- 商品图片 - 单独一行 --> <!-- 商品图片 - 单独一行 -->
<el-form-item v-if="publish_type === '1'" :label="publish_type === '2' ? '需求图片' : '商品图片'" prop="img" <el-form-item v-if="publish_type === '1'" :label="publish_type === '2' ? '需求图片' : '商品图片'" prop="img"
@ -72,7 +75,7 @@
</el-form-item> </el-form-item>
</div> </div>
<div class="form-row"> <div class="form-row">
<el-form-item label="GPU支持" prop="cart_flag" required class="form-item-half"> <el-form-item label="GPU支持" prop="cart_flag" required class="form-item-half">
<el-radio-group v-model="form.cart_flag"> <el-radio-group v-model="form.cart_flag">
<el-radio label="1"></el-radio> <el-radio label="1"></el-radio>
<el-radio label="0"></el-radio> <el-radio label="0"></el-radio>
@ -124,29 +127,31 @@
</el-form-item> </el-form-item>
</div> </div>
<div class="form-row"> <div class="form-row">
<el-form-item label="折扣" class="full-width"> <el-form-item label="折扣" class="full-width">
<el-input-number style="width: 120px;" :controls="false" v-model="form.discount" :precision="2" :step="0.01" :max="10"> <el-input-number style="width: 120px;" :controls="false" v-model="form.discount" :precision="2"
:step="0.01" :max="10">
</el-input-number> </el-input-number>
<span style="margin-left: 10px; font-weight: bold;"> </span> <span style="margin-left: 10px; font-weight: bold;"> </span>
<span style="margin-left: 10px; font-weight: bold;font-size: 14px;">折后价: <span style="margin-left: 10px; font-weight: bold;font-size: 14px;">折后价:
<span v-if="form.discount!==0&&form.price" style="color: red;">{{ (form.price * form.discount/10).toFixed(2 ) }}</span> <span v-if="form.discount !== 0 && form.price" style="color: red;">{{ (form.price *
<span v-else>-</span> form.discount/10).toFixed(2 ) }}</span>
</span> <span v-else>-</span>
</span>
<!-- <el-input v-model="form.discount" placeholder="请输入折扣" type="number"> <!-- <el-input v-model="form.discount" placeholder="请输入折扣" type="number">
<template slot="append"></template> <template slot="append"></template>
</el-input> --> </el-input> -->
</el-form-item> </el-form-item>
<el-form-item class="full-width"> <el-form-item class="full-width">
<template slot="label"> <template slot="label">
<span>平台服务费用</span> <span>平台服务费用</span>
<el-tooltip <el-tooltip content="平台服务费用为商品价格的3%,如有疑问请联系客服进行处理。" placement="top" effect="light">
content="平台服务费用为商品价格的3%,如有疑问请联系客服进行处理。" <i class="el-icon-warning-outline"
placement="top" style="margin-left: 5px; color: #333; cursor: pointer;"></i>
effect="light">
<i class="el-icon-warning-outline" style="margin-left: 5px; color: #333; cursor: pointer;"></i>
</el-tooltip> </el-tooltip>
</template> </template>
<span v-if="form.discount!==0&&form.price" style="color: red;font-size: 14px;font-weight: bold;">{{ ((form.price * form.discount/10)*0.03).toFixed(2) }}</span> <span v-if="form.discount !== 0 && form.price" style="color: red;font-size: 14px;font-weight: bold;">{{
((form.price *
form.discount/10)*0.03).toFixed(2) }}</span>
<span v-else>-</span> <span v-else>-</span>
</el-form-item> </el-form-item>
@ -169,10 +174,11 @@
<!-- 提交按钮 --> <!-- 提交按钮 -->
<div class="form-actions"> <div class="form-actions">
<el-button type="primary" @click="submitForm" size="large">{{ isEdit ? '编辑商品' : (publish_type === '2' ? '发布需求' : '发布商品') <el-button type="primary" @click="submitForm" size="large">
}}</el-button> {{ isEdit ? (publish_type === '2' ? '保存需求' : '保存商品') : (publish_type === '2' ? '发布需求' : '发布商品') }}
<el-button v-if="isEdit" @click="editBtn" size="large">确定修改</el-button> </el-button>
<!-- <el-button @click="resetForm" size="large">重置</el-button> --> <el-button @click="resetForm" size="large">重置</el-button>
<el-button @click="handleClose" size="large">关闭</el-button>
<!-- <el-button type="info" @click="getBinaryData" size="large">获取二进制数据</el-button> --> <!-- <el-button type="info" @click="getBinaryData" size="large">获取二进制数据</el-button> -->
</div> </div>
</el-form> </el-form>
@ -208,7 +214,7 @@
<script> <script>
import { VueCropper } from 'vue-cropper' import { VueCropper } from 'vue-cropper'
import { buildCaTree } from './buildCaTree' import { buildCaTree } from './buildCaTree'
import { reqGetProductCategorySearch, reqPublishProductAdd, reqCompanyCategorySearch } from '@/api/ncmatch' import { reqGetProductCategorySearch, reqPublishProductAdd, reqCompanyCategorySearch, reqEditProduct } from '@/api/ncmatch'
export default { export default {
name: 'sendProduct', name: 'sendProduct',
props: { props: {
@ -219,6 +225,10 @@ export default {
isEdit: { isEdit: {
type: Boolean, type: Boolean,
default: false default: false
},
editData: {
type: Object,
default: () => ({})
} }
}, },
components: { components: {
@ -264,8 +274,9 @@ export default {
application_scenario: "",// application_scenario: "",//
unit: "",// unit: "",//
short_term: "",// short_term: "",//
discount:null,// discount: null,//
cart_flag: '1',//GPU cart_flag: '1',//GPU
reject_reason: "",//
}, },
rules: { rules: {
product_name: [ product_name: [
@ -339,10 +350,75 @@ export default {
created() { created() {
this.init_product_category() this.init_product_category()
this.init_company_category() this.init_company_category()
//
if (this.isEdit && this.editData && Object.keys(this.editData).length > 0) {
Promise.all([
this.init_product_category(),
this.init_company_category()
]).then(() => {
this.$nextTick(() => {
this.fillEditData(this.editData);
});
});
}
},
watch: {
editData: {
handler(newVal) {
if (newVal && Object.keys(newVal).length > 0 && this.isEdit) {
//
this.$nextTick(() => {
this.fillEditData(newVal);
});
}
},
immediate: false, // false
deep: true
}
}, },
methods: { methods: {
//
fillEditData(data) {
//
if (data.img) {
this.selectedImage = data.img;
this.form.img = data.img;
}
//
Object.keys(this.form).forEach(key => {
if (data[key] !== undefined && data[key] !== null) {
this.form[key] = data[key];
}
});
//
if (data.company_type) {
//
if (Array.isArray(data.company_type)) {
this.form.company_type = [...data.company_type];
} else if (typeof data.company_type === 'string') {
//
if (data.company_type.includes(',')) {
this.form.company_type = data.company_type.split(',').map(item => item.trim());
} else {
this.form.company_type = [data.company_type];
}
}
}
if (data.product_category && Array.isArray(data.product_category)) {
this.form.product_category = [...data.product_category];
}
console.log('编辑数据已填充:', this.form);
console.log('公司类别数据:', data.company_type);
console.log('处理后的公司类别:', this.form.company_type);
},
init_company_category() { init_company_category() {
reqCompanyCategorySearch({ url_link: window.location.href }).then(res => { return reqCompanyCategorySearch({ url_link: window.location.href }).then(res => {
if (res.status) { if (res.status) {
this.company_category_list = [] this.company_category_list = []
for (let item of res.data) { for (let item of res.data) {
@ -351,8 +427,8 @@ export default {
value: item.company_category value: item.company_category
}) })
} }
} }
return res;
}) })
}, },
// base64Blob // base64Blob
@ -368,11 +444,11 @@ export default {
init_product_category() { init_product_category() {
// publish_type to_page // publish_type to_page
return reqGetProductCategorySearch({ url_link: window.location.href, to_page: 'publish' }).then(res => {
reqGetProductCategorySearch({ url_link: window.location.href, to_page: 'publish' }).then(res => {
if (res.status) { if (res.status) {
this.typeList = buildCaTree(res.data) this.typeList = buildCaTree(res.data)
} }
return res;
}) })
}, },
// //
@ -512,22 +588,49 @@ export default {
if (valid) { if (valid) {
console.log('表单数据:', this.form) console.log('表单数据:', this.form)
console.log('发布类型:', this.publish_type) console.log('发布类型:', this.publish_type)
console.log('是否为编辑模式:', this.isEdit)
let formdata = new FormData(); let formdata = new FormData();
for (let key in this.form) { for (let key in this.form) {
formdata.append(key, this.form[key]); formdata.append(key, this.form[key]);
} }
formdata.append('publish_type', this.publish_type) formdata.append('publish_type', this.publish_type)
//
// formdata.append('publish_type', this.publish_type);
reqPublishProductAdd(formdata).then(res => { //
if (res.status) { if (this.isEdit) {
this.$emit('success'); formdata.append('id', this.editData.id || '');
const successMessage = this.publish_type === '2' ? '添加需求成功!' : '添加产品成功!' reqEditProduct(formdata).then(res => {
this.$message.success(successMessage) if (res.status) {
} this.$emit('success');
}) //
// const successMessage = this.publish_type === '2' ? '' : ''
// this.$message.success(successMessage)
} else {
this.$message.error(res.msg || '编辑失败,请重试')
}
}).catch(error => {
console.error('编辑请求失败:', error);
this.$message.error('编辑失败,请检查网络连接后重试')
})
} else {
reqPublishProductAdd(formdata).then(res => {
if (res.status) {
this.$emit('success');
const successMessage = this.publish_type === '2' ? '添加需求成功!' : '添加产品成功!'
this.$message.success(successMessage)
} else {
this.$message.error(res.msg || '添加失败,请重试')
}
}).catch(error => {
loading.close();
console.error('添加请求失败:', error);
this.$message.error('添加失败,请检查网络连接后重试')
})
}
} else { } else {
this.$message.error('请完善表单信息') this.$message.error('请完善表单信息')
} }
@ -540,10 +643,51 @@ export default {
if (this.selectedImage) { if (this.selectedImage) {
URL.revokeObjectURL(this.selectedImage) URL.revokeObjectURL(this.selectedImage)
} }
//
this.$refs.form.resetFields() this.$refs.form.resetFields()
this.selectedImage = null
this.form.img = null //
this.$refs.fileInput.value = '' if (this.isEdit && this.editData && Object.keys(this.editData).length > 0) {
this.$nextTick(() => {
this.fillEditData(this.editData);
});
} else {
//
this.selectedImage = null
this.form.img = null
this.$refs.fileInput.value = ''
//
this.form = {
url_link: window.location.href,
img: null,
product_name: "",
product_category: "",
company_name: "",
company_type: [],
contact_person: "",
job_title: "",
phone_number: "",
email: "",
cpu: "",
memory: "",
gpu: "",
sys_disk: "",
data_disk: "",
net_card: "",
priority: "",
price: "",
unit: "",
short_term: "",
label: "",
requirement_summary: "",
related_parameters: "",
application_scenario: "",
discount: null,
cart_flag: '1',
}
}
}, },
// //
@ -596,6 +740,9 @@ export default {
console.log('没有二进制数据') console.log('没有二进制数据')
return null return null
} }
},
handleClose() {
this.$emit('close');
} }
} }
} }
@ -1002,4 +1149,25 @@ export default {
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'); 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');
} }
} }
.tip{
font-size: 16px;
font-weight: bold;
color: red;
padding: 10px;
border-radius: 5px;
background-color: #ecf7fe;
position: relative;
overflow: hidden;
margin-bottom: 20px;
&::after{
content: '';
position: absolute;
top: 0;
left: 2px;
transform: translateX(-50%);
width: 5px;
height: 100%;
background-color: rgb(189, 83, 83);
}
}
</style> </style>

View File

@ -5,7 +5,7 @@
{{ productDetailInfo.product_name }} {{ productDetailInfo.product_name }}
</h1> </h1>
<button class="consult-btn" @click="openTalk">立即咨询</button> <button v-if="!isApprove" class="consult-btn" @click="openTalk">立即咨询</button>
</div> </div>
<!-- 标题区域 --> <!-- 标题区域 -->
<ul class="title-section"> <ul class="title-section">
@ -139,6 +139,12 @@
import { mapState } from "vuex"; import { mapState } from "vuex";
export default { export default {
name: 'proDetail', name: 'proDetail',
props: {
isApprove: {
type: Boolean,
default: false
}
},
data() { data() {
return { return {

View File

@ -2,40 +2,69 @@
<div <div
style="display: flex;align-items: center;justify-content: center;border:1px solid red;width: 100%;max-width: 1400px;"> style="display: flex;align-items: center;justify-content: center;border:1px solid red;width: 100%;max-width: 1400px;">
<img style="width: 180px;height: 60px;padding-right: 20px;" src="https://www.kaiyuancloud.cn/idfile?path=logo_ncmatch.png" alt=""> <img @click="goHome" class="logo-clickable" style="width: 180px;height: 60px;padding-right: 20px;"
src="https://www.kaiyuancloud.cn/idfile?path=logo_ncmatch.png" alt="">
<div <div
style="min-width:800px;border-bottom: 1px solid #E5E5E5;display: flex;align-items: center;justify-content: space-between;"> style="min-width:800px;border-bottom: 1px solid #E5E5E5;display: flex;align-items: center;justify-content: space-between;">
<!-- 中间搜索区域 --> <!-- 中间搜索区域 -->
<div class="search-section" style="position: relative;margin: 0 20px;"> <div class="search-section" style="position: relative;margin: 0 20px;">
<div class="search-bar"> <div class="search-bar" :class="{ 'has-results': showSearchResults && searchResults.length > 0 }">
<input v-model="searchKeyword" type="text" class="search-input" placeholder="搜你想搜..."> <el-select class="mySelect" v-model="publish_type" placeholder="请选择" @change="handleTypeChange"
style="width: 75px;">
<el-option label="商品" value="1">
</el-option>
<el-option label="需求" value="2"></el-option>
</el-select>
<input v-model="keyword" type="text" class="search-input" placeholder="搜你想搜..." @input="handleInputChange"
@focus="handleInputFocus" @blur="handleInputBlur">
<button class="search-btn" @click="handleSearch">搜索</button> <button class="search-btn" @click="handleSearch">搜索</button>
</div> </div>
<!-- 实时搜索结果 -->
<div v-if="showSearchResults && searchResults.length > 0" class="search-results" @click.stop>
<div v-for="result in searchResults" :key="result.id" class="search-result-item"
@click.stop.prevent="handleSearch(result)"
@mousedown.stop.prevent
@mouseup.stop.prevent
style="cursor: pointer; padding: 10px">
<span class="result-title" v-html="highlightKeyword(result)"></span>
</div>
</div>
<!-- 热搜关键词 --> <!-- 热搜关键词 -->
<div class="hot-search"> <div class="hot-search">
<a v-for="keyword in hotSearchKeywords" :key="keyword" href="#" class="hot-keyword"> <span class="search-label">热门搜索:</span>
<a v-for="keyword in hotSearchKeywords" :key="keyword" href="#" class="hot-keyword"
@click="handleHotKeywordClick(keyword)">
{{ keyword }} {{ keyword }}
</a> </a>
</div> </div>
</div> </div>
<!-- position: absolute;right: -150px;top: 0px; --> <!-- position: absolute;right: -150px;top: 0px; -->
<span <span
style="height: 44px;line-height: 44px;border-radius: 8px;border:1px solid #275AFF; color: #275AFF;font-size: 18px;font-weight: 500;padding: 0 20px;z-index: 10;display: flex;align-items: center;gap: 10px;"> style="height: 44px;line-height: 44px;border-radius: 8px;border:1px solid #275AFF; color: #275AFF;font-size: 18px;font-weight: 500;padding: 0 20px;z-index: 10;display: flex;align-items: center;gap: 10px;">
<img style="width: 24px;height: 24px;" src="../mainPage/img/robot.svg" alt=""> <img style="width: 24px;height: 24px;" src="../mainPage/img/robot.svg" alt="">
NC AI</span> NC AI</span>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import eventBus from '@/utils/eventBus'
import { reqSearch } from '@/api/ncmatch'
export default { export default {
name: 'search', name: 'search',
data() { data() {
return { return {
keyword: '',
publish_type: '1',
showSearchResults: false,
searchResults: [],
searchTimeout: null,
//
//
hotSearchKeywords: [ hotSearchKeywords: [
'昆仑芯-P800', '昆仑芯-P800',
'天垓-150', '天垓-150',
@ -86,6 +115,444 @@ export default {
} }
], ],
} }
},
//
// watch
methods: {
goHome(event) {
if (this.$route.path != '/ncmatchHome/index') {
console.log('准备跳转到:', '/ncmatchHome/index');
this.$router.push('/ncmatchHome/index').then(() => {
console.log('跳转成功');
}).catch(err => {
console.error('跳转失败:', err);
});
} else {
console.log('已经在首页,无需跳转');
}
},
goSearch() {
//
const currentRoute = this.$route;
const currentDisplayPage = currentRoute.query.display_page;
const targetQuery = {
keyword: this.keyword,
publish_type: this.publish_type,
display_page: 'list', // 使 list
current_page: 1,
page_size: 8
};
console.log('准备跳转到搜索结果页面:', { currentRoute, targetQuery });
// display_page
if (currentDisplayPage !== 'list') {
//
console.log('执行路由跳转');
this.$router.push({
path: '/ncmatchHome/search',
query: targetQuery
}).catch(err => {
//
if (err.name !== 'NavigationDuplicated') {
console.error('路由导航错误:', err);
} else {
console.log('路由重复导航,忽略错误');
}
});
} else {
//
console.log('已在搜索结果页面,重新调用接口获取最新数据');
this.performFormalSearch(this.keyword, this.publish_type);
}
},
//
handleInputChange() {
//
if (this.searchTimeout) {
clearTimeout(this.searchTimeout)
}
//
if (!this.keyword.trim()) {
this.showSearchResults = false
this.searchResults = []
return
}
// 300ms
this.searchTimeout = setTimeout(() => {
this.performRandomSearch()
}, 300)
},
//
performRandomSearch() {
if (!this.keyword.trim()) {
this.showSearchResults = false
this.searchResults = []
return
}
console.log('执行联想搜索:', { keyword: this.keyword, type: this.publish_type });
//
this.performSearch(this.keyword, this.publish_type);
},
//
performSearch(keyword, type) {
console.log('执行联想搜索:', { keyword, type });
reqSearch({
url_link: window.location.href,
keyword: keyword,
publish_type: type,
display_page: 'overview', // 使 overview
current_page: 1,
page_size: 15
}).then(res => {
this.searchResults = res.data.result
console.log('联想搜索结果:', res)
this.showSearchResults = true
// URL
console.log('联想搜索完成,显示结果');
}).catch(error => {
console.error('联想搜索失败:', error);
this.$message.error('搜索失败,请重试');
});
},
//
performFormalSearch(keyword, type) {
console.log('=== 执行正式搜索开始 ===');
console.log('搜索参数:', { keyword, type });
console.log('当前路由:', this.$route.path);
console.log('事件总线实例:', eventBus);
reqSearch({
url_link: window.location.href,
keyword: keyword,
publish_type: type,
display_page: 'list', // 使 list
current_page: 1,
page_size: 8
}).then(res => {
console.log('正式搜索结果:', res);
//
const eventData = {
type: type,
keyword: keyword,
data: res.data
};
console.log('准备发送事件数据:', eventData);
// 线
try {
eventBus.$emit('search', eventData);
console.log('事件总线事件发送成功');
} catch (error) {
console.error('事件总线事件发送失败:', error);
}
console.log('正式搜索完成,已触发搜索事件');
}).catch(error => {
console.error('正式搜索失败:', error);
this.$message.error('搜索失败,请重试');
});
},
// URL
updateURLParams(keyword, type) {
const query = { ...this.$route.query };
if (keyword) {
query.keyword = keyword;
} else {
delete query.keyword;
}
if (type) {
query.publish_type = type;
} else {
delete query.publish_type;
}
console.log('更新URL参数:', {
oldQuery: this.$route.query,
newQuery: query
});
// URL
this.$router.replace({
path: this.$route.path,
query: query
}).catch(err => {
//
if (err.name !== 'NavigationDuplicated') {
console.error('更新URL参数失败:', err);
} else {
console.log('URL参数更新重复忽略错误');
}
});
},
//
extractPlainText(htmlText) {
// DOM
const tempDiv = document.createElement('div');
tempDiv.innerHTML = htmlText;
return tempDiv.textContent || tempDiv.innerText || '';
},
//
handleTypeChange(value) {
console.log('选择的类型:', value === '1' ? '商品' : '需求', value)
this.publish_type = value
//
if (this.keyword.trim() && this.showSearchResults) {
this.performSearch(this.keyword, value);
}
},
//
handleSearch(result) {
//
if (event) {
event.stopPropagation();
event.preventDefault();
}
console.log('=== handleSearch 开始执行 ===');
console.log('接收到的参数:', result);
console.log('当前路由路径:', this.$route.path);
console.log('当前查询参数:', this.$route.query);
// result使product_namekeyword
if (result && result.product_name) {
console.log('使用联想结果作为搜索关键词');
const plainText = this.extractPlainText(result.product_name);
this.keyword = plainText;
console.log('提取的纯文本:', plainText);
//
this.showSearchResults = false;
this.searchResults = [];
} else {
console.log('使用输入框的关键词进行搜索');
// result
if (!this.keyword.trim()) {
this.$message.warning('请输入搜索关键词');
return;
}
}
//
console.log('准备执行搜索,关键词:', this.keyword, '类型:', this.publish_type);
//
const currentRoute = this.$route;
const currentDisplayPage = currentRoute.query.display_page;
if (currentDisplayPage !== 'list') {
//
console.log('跳转到搜索结果页面');
this.goSearch()
} else {
//
console.log('已在搜索结果页面,重新调用接口获取最新数据');
// URL
this.updateURLParams(this.keyword, this.publish_type);
//
this.performFormalSearch(this.keyword, this.publish_type);
}
console.log('=== handleSearch 执行完成 ===');
},
//
handleHotKeywordClick(keyword) {
this.keyword = keyword
this.handleSearch()
},
//
handleInputFocus() {
//
if (this.keyword.trim() && this.searchResults.length > 0) {
this.showSearchResults = true;
}
},
//
handleInputBlur() {
//
setTimeout(() => {
if (!this.$el.contains(document.activeElement)) {
this.hideSearchResults()
}
}, 100); // 100ms
},
//
hideSearchResults() {
this.showSearchResults = false
this.searchResults = []
},
//
handleGlobalClick(event) {
//
setTimeout(() => {
//
if (this.$el && this.$el.contains(event.target)) {
console.log('点击在搜索组件内部,不隐藏联想结果');
return;
}
//
if (this.showSearchResults) {
console.log('点击在搜索组件外部,隐藏联想结果');
this.hideSearchResults();
}
}, 100); // 100ms
},
//
highlightKeyword(item) {
console.log(item)
if (!this.keyword || !this.keyword.trim()) {
return item.product_name || item;
}
const keyword = this.keyword.trim();
// 使 product_name
const text = item.product_name || item;
console.log('高亮处理:', { keyword, text, item, textType: typeof text });
//
if (!keyword) {
return text;
}
//
if (!text || typeof text !== 'string') {
console.warn('文本不是字符串类型:', text);
return text;
}
//
const keywords = keyword.split(/\s+/).filter(k => k.length > 0);
let highlightedText = text;
//
keywords.forEach(key => {
if (key.length > 0) {
const regex = new RegExp(`(${this.escapeRegExp(key)})`, 'gi');
highlightedText = highlightedText.replace(regex, '<span class="highlighted-keyword">$1</span>');
}
});
console.log('高亮结果:', highlightedText);
return highlightedText;
},
//
escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
},
//
handleRouteChange(to, from) {
console.log('处理路由变化:', { to, from, currentKeyword: this.keyword });
const keyword = to.query.keyword;
const publishType = to.query.publish_type;
if (keyword) {
//
console.log('更新搜索状态:', { keyword, publishType });
this.keyword = keyword;
this.publish_type = publishType || '1'; //
//
this.showSearchResults = false;
this.searchResults = [];
//
this.$nextTick(() => {
this.reinitializeEventListeners();
});
//
//
console.log('搜索状态已更新,等待用户操作');
} else {
//
if (this.keyword || this.publish_type !== '1') {
console.log('清空搜索状态');
this.keyword = '';
this.publish_type = '1';
this.hideSearchResults();
}
}
},
//
reinitializeEventListeners() {
console.log('重新初始化事件监听器');
//
document.removeEventListener('click', this.handleGlobalClick);
//
document.addEventListener('click', this.handleGlobalClick);
//
this.showSearchResults = false;
this.searchResults = [];
console.log('事件监听器重新初始化完成');
}
},
//
mounted() {
console.log('=== 组件挂载开始 ===');
console.log('当前路由:', this.$route.path);
console.log('当前查询参数:', this.$route.query);
document.addEventListener('click', this.handleGlobalClick)
console.log('全局点击事件监听器已添加');
// URL
this.handleRouteChange(this.$route, null);
//
this.$watch('$route', (to, from) => {
console.log('路由变化监听:', { to, from });
this.handleRouteChange(to, from);
}, { immediate: true, deep: true });
console.log('=== 组件挂载完成 ===');
},
//
beforeDestroy() {
if (this.searchTimeout) {
clearTimeout(this.searchTimeout)
}
document.removeEventListener('click', this.handleGlobalClick)
} }
} }
</script> </script>
@ -95,6 +562,7 @@ export default {
border: 1px solid red; border: 1px solid red;
flex: 1; flex: 1;
min-width: 850px; min-width: 850px;
.search-bar { .search-bar {
display: flex; display: flex;
align-items: center; align-items: center;
@ -102,6 +570,14 @@ export default {
border: 2px solid #3f68d8; border: 2px solid #3f68d8;
border-radius: 4px; border-radius: 4px;
overflow: hidden; overflow: hidden;
position: relative;
transition: border-radius 0.2s ease;
//
&.has-results {
border-bottom-color: transparent;
border-radius: 4px 4px 0 0;
}
.search-input { .search-input {
flex: 1; flex: 1;
@ -109,12 +585,7 @@ export default {
border: none; border: none;
outline: none; outline: none;
font-size: 14px; font-size: 14px;
} padding-left: 0;
.camera-icon {
padding: 0 10px;
color: #666;
cursor: pointer;
} }
.search-btn { .search-btn {
@ -131,12 +602,68 @@ export default {
} }
} }
//
.search-results {
position: absolute;
top: 42px;
left: 0;
right: 0;
background: white;
border: 2px solid #3f68d8;
border-top: none;
border-radius: 0 0 4px 4px;
z-index: 99999;
max-height: 300px;
overflow-y: auto;
.search-result-item {
padding: 12px 15px;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
&:hover {
background-color: #f5f7fa;
* {
color: #0864dd!important;
}
}
&:last-child {
border-bottom: none;
}
.result-title {
font-size: 14px;
color: #333;
}
.result-type {
font-size: 12px;
color: #909399;
padding: 2px 8px;
background: #f0f2f5;
border-radius: 10px;
}
}
}
.hot-search { .hot-search {
margin-top: 8px; margin-top: 8px;
display: flex; display: flex;
gap: 15px; gap: 15px;
flex-wrap: wrap; flex-wrap: wrap;
.search-label {
font-size: 14px;
color: #666;
margin-right: 10px;
}
.hot-keyword { .hot-keyword {
&:nth-child(-n+3) { &:nth-child(-n+3) {
color: #e1251b; color: #e1251b;
@ -153,6 +680,26 @@ export default {
} }
} }
::v-deep .mySelect {
input {
border: none;
}
}
::v-deep .highlighted-keyword {
font-weight: bold;
color: #0864dd;
}
.logo-clickable {
cursor: pointer;
transition: opacity 0.2s ease;
&:hover {
opacity: 0.8;
}
}
@media (max-width: 768px) { @media (max-width: 768px) {
.search-section { .search-section {
width: 100%; width: 100%;

View File

@ -0,0 +1,140 @@
<template>
<div class="searchBox">
<div v-if="loadData">
<!-- <div class="loading">
加载数据中...
</div> -->
</div>
<div v-else>
<div v-loading="loadData">
<productCard :productList="list"></productCard>
<el-pagination @current-change="handleCurrentChange" :current-page="current_page" :page-size="page_size"
layout="total, prev, pager, next" :total="total">
</el-pagination>
</div>
</div>
</div>
</template>
<script>
import { reqSearch } from '@/api/ncmatch'
import productCard from '../mainPage/productCard/index.vue'
import eventBus from '@/utils/eventBus'
export default {
name: 'searchBox',
components: {
productCard
},
data(){
return {
keyword: '',
publish_type: '',
display_page: '',
current_page: 1,
page_size: 8,
list:[],
total:0,
loadData:false
}
},
created(){
console.log('=== searchBox 组件创建 ===');
this.keyword = this.$route.query.keyword
this.publish_type = this.$route.query.publish_type
this.initData()
//
console.log('开始监听搜索事件');
eventBus.$on('search', this.handleSearch)
console.log('搜索事件监听器已添加');
},
mounted() {
console.log('=== searchBox 组件挂载 ===');
console.log('当前路由:', this.$route.path);
console.log('当前查询参数:', this.$route.query);
},
beforeDestroy() {
console.log('=== searchBox 组件销毁 ===');
//
eventBus.$off('search', this.handleSearch)
console.log('搜索事件监听器已移除');
},
methods: {
handleCurrentChange(val) {
console.log('分页变化:', val);
this.current_page = val;
this.initData();
},
handleSearch(searchData) {
console.log('=== searchBox 接收到搜索事件 ===');
console.log('搜索数据:', searchData);
console.log('当前组件状态:', {
keyword: this.keyword,
publish_type: this.publish_type,
list_length: this.list.length,
total: this.total
});
//
this.current_page = 1;
console.log('分页已重置到第1页');
//
if (searchData.data && searchData.data.result) {
console.log('更新搜索结果数据');
this.list = searchData.data.result;
this.total = searchData.data.total_count;
console.log('数据更新完成:', {
new_list_length: this.list.length,
new_total: this.total
});
} else {
console.warn('搜索数据格式不正确:', searchData);
}
//
this.keyword = searchData.keyword;
this.publish_type = searchData.type;
console.log('本地状态已更新:', {
new_keyword: this.keyword,
new_publish_type: this.publish_type
});
console.log('=== 搜索事件处理完成 ===');
},
initData(){
this.loadData = true
reqSearch({
url_link: window.location.href,
keyword: this.keyword,
publish_type: this.publish_type,
display_page: 'list',
current_page: this.current_page,
page_size: this.page_size
}).then(res => {
console.log("@@@@",res);
this.loadData = false
if(res.status){
this.list = res.data.result
this.total = res.data.total_count
}
})
}
}
}
</script>
<style scoped lang="scss">
.searchBox {
width: 100%;
}
</style>
<style lang="scss">
.searchBox {
width: 100%;
}
</style>

View File

@ -0,0 +1,326 @@
<template>
<div>
<!-- 调试信息 -->
<div style="margin-bottom: 20px; padding: 15px; background: #f5f7fa; border-radius: 8px;">
<el-button size="small" @click="showDebugInfo">查看调试信息</el-button>
<el-button size="small" @click="showRawData">查看原始数据</el-button>
<el-button size="small" type="primary" @click="testTreeStructure">测试树形结构</el-button>
<el-button size="small" type="success" @click="getCategories">重新加载数据</el-button>
<span style="margin-left: 20px; color: #666;">
数据总数: {{ tableData.length }} |
树形层级: {{ getMaxLevel() }}
</span>
</div>
<el-table :data="tableData" style="width: 100%;margin-bottom: 20px;" row-key="id" border v-loading="loading"
element-loading-text="加载中..." element-loading-spinner="el-icon-loading"
:tree-props="{ children: 'children' }" :default-expand-all="false" :expand-on-click-node="false">
<el-table-column prop="name" label="名称" min-width="120">
</el-table-column>
<el-table-column prop="id" label="id" min-width="200">
</el-table-column>
<el-table-column prop="level" label="层级" min-width="120">
</el-table-column>
<el-table-column label="操作" width="280" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="small" @click="addNode(scope.row, 'sibling')">新增同级</el-button>
<el-button type="text" size="small" @click="addNode(scope.row, 'child')">新增子级</el-button>
<el-button type="text" size="small" @click="editNode(scope.row)">编辑</el-button>
<el-button type="text" size="small" @click="deleteNode(scope.row)"
style="color: #F56C6C;">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-drawer title="菜单管理" :visible.sync="drawer" :direction="direction">
<div style="margin: 20px;"></div>
<el-form :label-position="labelPosition" label-width="80px" :model="formLabelAlign">
<el-form-item label="菜单名称">
<el-input v-model="formLabelAlign.name"></el-input>
</el-form-item>
</el-form>
</el-drawer>
</div>
</template>
<script>
import { reqNcMatchMenu } from '@/api/ncmatch';
export default {
name: 'menuMangement',
data() {
return {
loading: false,
categories: [],
tableData: [], //
drawer: false,
direction: 'rtl',
labelPosition: 'right',
formLabelAlign: {
name: '',
region: '',
type: ''
}
}
},
created() {
this.getCategories();
},
methods: {
addNode(row, type) {
this.drawer = true;
console.log('新增节点:', row, '类型:', type);
},
getCategories() {
this.loading = true;
reqNcMatchMenu({ url_link: window.location.href }).then(res => {
this.loading = false;
if (res.status) {
this.categories = this.buildSimpleTree(res.data);
this.tableData = this.categories; // 使
console.log("构建的精简树结构:", this.categories);
}
}).catch(error => {
this.loading = false;
console.error('获取菜单数据失败:', error);
});
},
editNode(row) {
this.drawer = true;
console.log('编辑节点:', row);
},
deleteNode(row) {
this.drawer = true;
console.log('删除节点:', row);
},
buildTree(data, parentId = null) {
if (!Array.isArray(data)) {
console.log('数据不是数组:', data);
return [];
}
console.log('构建树结构当前parentId:', parentId, '数据长度:', data.length);
//
const currentNodes = data.filter(item => {
// parentid null
if (parentId === null) {
return !item.parentid || item.parentid === '' || item.parentid === '0';
}
return item.parentid === parentId;
});
console.log('当前层级节点数量:', currentNodes.length, '节点:', currentNodes);
if (currentNodes.length === 0) return [];
//
return currentNodes.map(node => {
const resultNode = {
id: node.id,
name: node.name || '未命名',
parentid: node.parentid
};
//
const children = this.buildSimpleTree(data, node.id);
// children
if (children && children.length > 0) {
resultNode.children = children;
}
return resultNode;
});
},
//
buildSimpleTree(data, parentId = null) {
if (!Array.isArray(data)) {
return [];
}
//
const currentNodes = data.filter(item => {
if (parentId === null) {
return !item.parentid || item.parentid === '' || item.parentid === '0';
}
return item.parentid === parentId;
});
if (currentNodes.length === 0) return [];
//
return currentNodes.map(node => {
const resultNode = {
id: node.id,
name: node.name || '未命名',
parentid: node.parentid
};
//
const children = this.buildSimpleTree(data, node.id);
// children
if (children && children.length > 0) {
resultNode.children = children;
}
return resultNode;
});
},
//
calculateLevel(data, nodeId) {
let level = 1;
let currentId = nodeId;
while (true) {
const parent = data.find(item => item.id === currentId);
if (!parent || !parent.parentid || parent.parentid === '' || parent.parentid === '0') {
break;
}
level++;
currentId = parent.parentid;
}
return level;
},
showDebugInfo() {
console.log("当前 tableData 的结构:", this.tableData);
console.log("当前 tableData 的层级分布:", this.getLevelDistribution());
console.log("当前 tableData 的最大层级:", this.getMaxLevel());
//
this.checkTreeStructure(this.tableData);
},
//
checkTreeStructure(nodes, level = 0) {
if (!Array.isArray(nodes)) return;
nodes.forEach(node => {
const indent = ' '.repeat(level);
console.log(`${indent}📁 ${node.name} (ID: ${node.id})`);
console.log(`${indent} - hasChildren: ${node.hasChildren}`);
console.log(`${indent} - children.length: ${node.children ? node.children.length : 'undefined'}`);
console.log(`${indent} - level: ${node.level}`);
if (node.children && node.children.length > 0) {
this.checkTreeStructure(node.children, level + 1);
}
});
},
showRawData() {
console.log("原始数据:", this.categories);
},
//
testTreeStructure() {
const testData = [
{
id: 1,
name: '一级菜单1',
parentid: null
},
{
id: 2,
name: '一级菜单2',
parentid: null
},
{
id: 3,
name: '一级菜单3',
parentid: null,
children: [
{
id: 31,
name: '二级菜单3-1',
parentid: 3
},
{
id: 32,
name: '二级菜单3-2',
parentid: 3,
children: [
{
id: 321,
name: '三级菜单3-2-1',
parentid: 32
}
]
}
]
}
];
console.log("精简测试数据:", testData);
this.tableData = testData;
},
getLevelDistribution() {
const levels = {};
this.tableData.forEach(node => {
const level = node.level;
if (levels[level]) {
levels[level]++;
} else {
levels[level] = 1;
}
});
return levels;
},
getMaxLevel() {
let maxLevel = 0;
this.tableData.forEach(node => {
if (node.level > maxLevel) {
maxLevel = node.level;
}
});
return maxLevel;
}
}
}
</script>
<style scoped>
.el-table {
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.el-table th {
background-color: #f5f7fa;
color: #606266;
font-weight: 600;
}
.el-table td {
padding: 12px 0;
}
.el-button--text {
padding: 4px 8px;
margin: 0 2px;
}
.el-button--text:hover {
background-color: #f5f7fa;
border-radius: 4px;
}
.el-tag {
border-radius: 4px;
}
/* 树形表格的缩进样式 */
.el-table__expand-icon {
margin-right: 8px;
}
/* 加载状态样式 */
.el-loading-mask {
background-color: rgba(255, 255, 255, 0.8);
}
</style>