以下为前端主页面
<template>
<view>
<view class="flex" id="headinga">
<view class="menu-left">
<view class="menu-item" @click="ontoView(index)" :class="{'menu-select':menuSelect==index}"
v-for="(item,index) in productList" :key="index">
<view>{{item.name}}</view>
</view>
</view>
<view class="menu-right">
<view class="relative">
<view class="cu-bar search bg-white">
<view class="search-form round">
<text class="cuIcon-search"></text>
<input @blur="searchInputBlur" @focus="searchInputFocus" v-model="searchKeyword"
:adjust-position="false" type="text" placeholder="请输入产品名称或型号"
confirm-type="search"></input>
</view>
<view class="action" @click="getSearchRecommendList('sign')">
<button class="cu-btn bg-blue shadow-blur round">搜索</button>
</view>
</view>
<view class="padding-lr-sm bg-white relative" v-show="searchRecommendShow">
<view class="border-bottom padding-tb-sm"
v-for="(searchRecommendItem,searchRecommendIndex) in searchRecommendList"
:key="searchRecommendIndex"
@click="getKeywordProductList(searchRecommendItem,searchRecommendIndex)">
{{searchRecommendItem.name}}
</view>
</view>
</view>
<view class="padding-lr-sm" v-show="searchRecommendShow == false">
<scroll-view :scroll-y="true" :enable-passive="true" :scroll-with-animation="true"
:enable-back-to-top="true" :style="'height:'+windowHeight+'px'" :scroll-into-view='toView'
@scroll="scroll" @scrolltoupper='scrolltoupper' @scrolltolower="scrolltolower">
<view
v-if="productList && productList[menuSelect] && productList[menuSelect].child_product_list"
class="bg-blue shadow radius10 padding-sm margin-tb-sm" @click="setProductArray(indexs)"
style="background-image: url(https://cdn.nlark.com/yuque/0/2019/png/280374/1552996358352-assets/web-upload/cc3b1807-c684-4b83-8f80-80e5b8a6b975.png);background-size: cover;background-position: center;"
v-for="(item,indexs) in productList[menuSelect].child_product_list" :key="indexs">
<view class="craditem">
<view class="margin-tb-xs text-df text-bold">{{item.name ? item.name : '暂无数据' }}
</view>
<view class="text margin-tb-sm">
产品型号:{{item.specification ? item.specification : '暂无数据'}}</view>
<view class="text margin-tb-xs">
流量扬程代码:{{item.traffic_code ? item.traffic_code : '暂无数据'}}</view>
<view class="text margin-tb-xs">
电机规格:{{item.electrical_specification ? item.electrical_specification : '暂无数据'}}
</view>
<view class="margin-tb-xs flex align-center justify-between margin-top">
<view></view>
<view class="flex justify-start align-center">
<view class="text-bold text-cut text-white text-right text-lg">
¥{{item.money}}
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</view>
<view>
<view class="cu-modal" :class="resultShow ? 'show' : ''">
<view class="cu-dialog padding-bottom">
<view class="solid-bottom text-xsl padding margin-top">
<text class="cuIcon-roundcheckfill text-green"></text>
<view class="margin-top-xs margin-bottom-sm text-bold text-lg">
获取报价成功
</view>
</view>
<view class="margin-top-lg text-bold text-lg">
此次报价合计价格为: ¥<text class="text-bold text-blue text-xl">{{quotationResult.sumMoney}}</text> 元
</view>
<view class="margin-top-sm text-bold text-lg margin-top-xl" @click="copyExcelUrl">
<text class="text-blue">点击复制链接</text>
</view>
<view class="cu-form-group margin-top">
<u-button type="primary" text="确定" color="#3a69fa" @click="closeResultDialog"></u-button>
</view>
</view>
</view>
</view>
<settle :productList="productArray" @delProductInfo="delProductInfo" @editProductInfo="editProductInfo"
@addProductInfo="addProductInfo" @editMoney="editMoney" @showResult="showResult"></settle>
<drag-button :isDock="true" :existTabBar="true" @btnClick="dragButtonClick" />
</view>
</template>
<script>
import common from '@/utils/common.js'
import config from '@/utils/config.js'
import settle from '@/components/settle/settle.vue'
import dragButton from '@/components/drag-button/drag-button.vue'
import {
getProductList,
getSearchRecommendList
} from "@/api/product.js"
export default {
components: {
settle,
dragButton
},
data() {
return {
miniResourceUrl: '',
//分类商品滚动参数开始
windowHeight: 0,
menuSelect: 1,
toView: 'menuItem1',
//分类商品滚动参数结束
productList: [], //产品列表
productArray: [], //购物车的商品列表
// money:0.00,
searchRecommendList: [], //搜索推荐列表
searchRecommendShow: false, //是否展示搜索推荐列表
searchKeyword: '', //搜索关键词
copyChildProductList: [], //选中的拷贝数据
resultShow: false, //是否展示结果
quotationResult: {}, //报价结果
}
},
watch: {
searchKeyword(newValue, oldValue) {
this.getSearchRecommendList();
}
},
onReady() {
this.onReadyApi()
},
created() {
this.miniResourceUrl = config.miniResourceUrl;
this.getProductList();
this.init();
},
onLoad() {},
methods: {
//初始化
init() {
const that = this;
let productList = JSON.parse(uni.getStorageSync('productList'));
if (productList.length > 0) {
uni.showModal({
title: '提醒',
content: '是否清空上次选择的产品?',
cancelText: '清空',
confirmText: '不清空',
success(res) {
if (res.cancel) {
uni.removeStorageSync('productList');
that.productArray = [];
} else if (res.confirm) {
that.productArray = productList; //缓存的数据
}
}
})
}
},
//悬浮窗点击事件
dragButtonClick() {
const that = this;
common.loading('刷新中')
that.getProductList();
uni.hideLoading();
},
//点击产品列表添加进购物车
setProductArray(index) {
const that = this;
let data = that.productList[that.menuSelect].child_product_list[index];
if (that.productArray.length) {
let isSetIndex = that.productArray.findIndex(item => item.id === data.id);
if (isSetIndex == -1) {
that.productArray.push(data);
} else {
that.productArray[isSetIndex].number++;
}
} else {
that.productArray.push(data);
}
},
//搜索框监听搜索获取产品列表
getSearchRecommendList(sign) {
const that = this;
// if(!that.searchKeyword && sign){
// common.toast('请输入您要搜索的产品名称或型号');
// return false;
// }
common.loading();
getSearchRecommendList({
cate_id: that.productList[that.menuSelect].id,
keyword: that.searchKeyword,
}).then(res => {
uni.hideLoading();
if (res.data.code) {
that.searchRecommendList = res.data.data.searchRecommendList;
}
})
},
//搜索补全点击事件
getKeywordProductList(item, index) {
const that = this;
that.productList[that.menuSelect].child_product_list = [item];
},
//搜索框聚焦事件
searchInputFocus(e) {
const that = this;
that.searchRecommendShow = true;
},
//搜索框失焦事件
searchInputBlur() {
const that = this;
// that.searchRecommendList = [];
// that.searchKeyword = '';
common.loading();
this.$nextTick(function() {
setTimeout(function() {
that.searchRecommendShow = false;
uni.hideLoading();
}, 500)
})
},
//获取商品列表
getProductList() {
const that = this;
common.loading();
getProductList({}).then(res => {
uni.hideLoading();
if (res.data.code) {
that.productList = res.data.data.productList;
if (that.productList && that.productList[that.menuSelect] && that.productList[that
.menuSelect].child_product_list) {
that.copyChildProductList = that.productList[that.menuSelect].child_product_list;
}
}
})
},
//获取屏幕高度
onReadyApi() {
const that = this;
let windowHeight = uni.getSystemInfoSync().windowHeight;
that.windowHeight = windowHeight;
},
//右侧滚动事件
scroll(event) {
const that = this;
var distanceArr = [];
var distanceHeight = [];
},
// 滚动到顶部
scrolltoupper() {
const that = this;
setTimeout(() => {
// that.menuSelect=0
}, 150)
},
// 滚动到顶部
scrolltolower() {
const that = this;
setTimeout(() => {
// that.menuSelect = that.availableList.length-1
}, 150)
},
//左侧点击事件
ontoView(index) {
const that = this;
that.menuSelect = index
that.toView = 'menuItem' + index;
that.copyChildProductList = that.productList[that.menuSelect].child_product_list; //原数据
},
//子组件删除服务项
delProductInfo(index) {
const that = this;
that.productArray.splice(index, 1);
},
//修改money
editMoney(money) {
const that = this;
that.money = money;
},
//获得excel文件展示报价结果
showResult(result) {
const that = this;
that.quotationResult = result;
that.resultShow = true;
},
//关闭结果弹窗
closeResultDialog() {
const that = this;
that.resultShow = false
},
//拷贝报价单地址
copyExcelUrl() {
const that = this;
uni.setClipboardData({
data: that.quotationResult.url + '', // 这里是个坑接受字符串类型 value转化为字符串
success: function() {
common.toast('复制成功');
},
})
},
}
}
</script>
<style lang="scss">
.menu-left {
width: 30%;
text-align: center;
background-color: #fff;
border-right: 1rpx solid #e5e5e5;
}
.menu-right {
width: 70%;
box-sizing: border-box;
}
.menu-item {
font-size: 28rpx;
color: #333;
padding: 25rpx 0;
border-bottom: 0.5rpx solid #e5e5e5;
}
.menu-select {
background-color: #3A69FA;
color: #fff;
}
.menu-title {
padding: 25rpx 0;
font-size: 28rpx;
color: #333;
}
.text {
font-size: 24rpx;
color: #fff;
}
.menu-crad:last-child {
padding-bottom: 300rpx;
}
.plus {
width: 40rpx;
height: 40rpx;
line-height: 40rpx;
text-align: center;
background-color: #fff;
color: #3A69FA;
font-size: 40rpx;
font-weight: bold;
border-radius: 50%;
justify-content: center;
align-items: center;
}
.cu-bar {
display: flex;
position: relative;
align-items: center;
min-height: 80rpx;
justify-content: space-between;
}
.cu-bar .search-form {
margin: 0rpx 20rpx;
}
.cu-bar .search-form input {
padding-right: 10rpx;
}
.cu-bar .search-form+.action {
margin-right: 20rpx;
}
</style>
以下为组件页面代码
<template>
<view>
<view class="settle-page" style="z-index: 99;">
<view class="flex align-center justify-between">
<view class="center flex-center" @click="lookProductList">
<view class="left-image relative">
<image style="width: 50rpx;height: 50rpx;" src="../../static/image/car.png" mode=""></image>
<view style="position: absolute;top:-15rpx;right: -20rpx;">
<u-badge type="warning" max="99" :value="productNumber"></u-badge>
</view>
</view>
<view>
<view>合计金额:<text class="center-money">¥{{money}}</text></view>
<view class="center-text">备注:(每个类别可以多选)</view>
</view>
</view>
<view class="right-button" @click="setteMent">确认提交</view>
</view>
</view>
<u-popup :show="show" :closeOnClickOverlay="true" @close="show = false">
<view>
<view class="popup padding-sm shadow">
<view class="flex align-center justify-between">
<view class="flex align-center justify-start">
<view>已选产品(<text class="text-blue">{{productList.length ? productList.length : 0}}</text>) 共计<text class="text-blue">{{productNumber ? productNumber : 0}} (件)</text></view>
<view class="text-blue margin-left-sm">¥{{money}}</view>
</view>
<u-icon name="arrow-down" size="18" @click="show = false"></u-icon>
</view>
<block v-if="productList.length > 0">
<view class="padding-lr-lg border-bottom padding-bottom-sm margin-top-lg">
<view class="cu-form-group text-blue round" style="border:1rpx solid #3A69FA;min-height: 80rpx;">
<input name="name" v-model="discount" placeholder="请输入折扣比例" placeholder-class="placeholder-style" style="color:#3A69FA;"></input>
折扣比例(%)
</view>
</view>
<scroll-view :scroll-y="true" :enable-passive="true" :scroll-with-animation="true"
:enable-back-to-top="true" style="height:800rpx" @scroll="scroll">
<view class="white padding-sm" v-for="(item,index) in productList" :key="index">
<view class="flex-align-center padding-xs" style="border-bottom:1rpx dashed #3A69FA;">
<view style="width: 85%;" class="margin-left">
<view style="lineHeight" class="text-bold">{{item.name}} * {{item.number}}</view>
<view style="lineHeight" class="margin-tb-sm">{{item.specification}}</view>
<view class="text-blue flex-between mgtop15">
<view class="text-bold">¥{{item.money}}</view>
<view class="flex justify-start align-center">
<u-icon name="trash" size="20" @click="delProductInfo(index)"></u-icon>
<view class="margin-left-sm">
<u-number-box v-model="item.number"></u-number-box>
</view>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</block>
<block>
<u-empty mode="car" :icon="miniResourceUrl + '/empty/newsEmpty.png'" text="暂无数据" style="position: absolute;"></u-empty>
</block>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import common from '@/utils/common.js'
import config from '@/utils/config.js'
import {
getQuotationExcel,
} from "@/api/product.js"
export default {
props: [
'productList',
],
data() {
return {
show: false,
miniResourceUrl: '',
isDel: false,
productNumber: 0,
money:0.00,
discount:null
}
},
watch: {
productList:{
handler(newValue) {
this.productListChange(newValue);
},
deep: true, //开启深度监听 默认为false
}
},
created() {
this.miniResourceUrl = config.miniResourceUrl;
},
onReady() {
},
methods: {
//删除服务项
delProductInfo(index) {
const that = this;
that.isDel = true;
that.productList.splice(index, 1);
this.$emit("delProductInfo", index)
},
//编辑服务项
editProductInfo(data, index) {
const that = this;
that.show = false;
this.$emit('editProductInfo', {
data: data,
index: index
})
},
//滚动监听
scroll(e) {
const that = this;
console.log(JSON.stringify(e));
},
//服务项选择改变事件
productListChange(productList) {
const that = this;
that.productList = productList;
uni.setStorageSync('productList', JSON.stringify(productList));
that.money = 0.00;
let money = 0;
that.productNumber = 0;
that.productList.forEach(value => {
money += Math.round(value.number * value.money * 100) / 100;
that.money = parseFloat(money * 1).toFixed(2);
that.productNumber += value.number * 1;
})
this.$emit('editMoney', that.money)
},
//查看已选择的服务项列表
lookProductList() {
const that = this;
console.log(JSON.stringify(that.productList));
that.show = true;
},
//确认按钮点击事件
setteMent() {
const that = this;
if(that.productList.length <= 0){
common.toast('请先选择产品');
return false;
}
if(!that.discount){
uni.showModal({
title:'提示',
content:'您当前未设置优惠折扣,是否确认提交?如需设置请取消后点击左下角购物车进行设置',
success(showModalRes) {
if(showModalRes.confirm){
that.getQuotationExcel();
}else{
common.toast('您已取消操作');
}
}
})
}else{
that.getQuotationExcel();
}
},
getQuotationExcel(){
const that = this;
common.loading();
getQuotationExcel({
product_list:that.productList,
discount:that.discount
}).then(res=>{
uni.hideLoading();
if(res.data.code){
that.$emit('showResult', res.data.data);
}else{
common.toast(res.data.msg);
}
})
}
}
}
</script>
<style scoped>
.settle-page {
position: fixed;
bottom: 0;
width: 100%;
border-top: 1rpx solid #efefef;
height: 100rpx;
background-color: #fff;
}
.flex-center {
display: flex;
align-items: center;
}
.flex-between {
display: flex;
align-items: center;
justify-content: space-between;
}
.left-image {
margin: -40rpx 20rpx 0 20rpx;
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
border: 15rpx solid #fff;
box-shadow: 0 0 5rpx #dddddd;
background-color: #3A69FA;
border-radius: 50%;
}
.center-money {
color: #3A69FA;
font-weight: bold;
}
.center-text {
color: #5f5f5f;
font-size: 24rpx;
margin-top: 10rpx;
}
.right-button {
height: 100rpx;
line-height: 100rpx;
text-align: center;
width: 30%;
background-color: #3A69FA;
color: #fff;
}
.popup {
height:900rpx;
border-top-left-radius: 30rpx;
border-top-right-radius: 30rpx;
padding-bottom: 100rpx;
}
.placeholder-style{
color: #3A69FAs;
}
</style>
以下为后端代码:
<?php
namespace app\api\controller;
use app\common\model\Cate as ProductCateModel;
use app\common\controller\Api;
use app\common\model\Product;
use app\common\model\Product as ProductModel;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use think\Db;
use think\Request;
class System extends Api
{
protected $noNeedLogin = ['getProductList', 'getSearchRecommendList', 'getQuotationExcel'];//无需登录无需鉴权
protected $noNeedRight = '*';//无需鉴权但需登录
public $param = [];
/**
* 构造方法
* Custom constructor.
* @param Request|null $request
*/
public function __construct(Request $request = null)
{
parent::__construct($request);
if ($this->request->isPost()) {
$post = $this->request->post();
if (!isset($post['data'])) $this->error('参数格式异常~', '', 0);
$this->param = $post['data'];
}
}
/**
* 获取商品列表
*/
public function getProductList()
{
try {
if (!$this->request->isPost()) throw new \Exception('非法请求');
$where = [
'status' => 1,
'parent_id' => 0
];
$productList = ProductCateModel::where($where)->order('id desc')->field('id,name')->cache()->select();
$productList = json_decode(json_encode($productList, true), true);
if (!empty($productList)) {
foreach ($productList as &$item) {
$item['child_product_list'] = ProductModel::where(['cate_id' => $item['id'], 'status' => 1])->cache()->select();
}
}
} catch (\Exception $e) {
$this->error($e->getMessage());
}
$this->success('获取商品列表成功', ['productList' => $productList]);
}
//搜索框监听搜索获取产品列表
public function getSearchRecommendList()
{
try {
$param = $this->param;
if (!isset($param['cate_id']) || !$param['cate_id']) {
throw new \Exception('参数错误');
}
if (!isset($param['keyword']) || !$param['keyword']) {
throw new \Exception('参数错误');
}
$searchRecommendList = ProductModel::where([
'cate_id' => ['eq', $param['cate_id']],
'name|specification' => ['like', '%' . $param['keyword'] . '%']
])->select();
$sql = ProductModel::getLastsql();
} catch (\Exception $e) {
$this->error($e->getMessage());
}
$this->success('获取成功', ['searchRecommendList' => $searchRecommendList, 'sql' => $sql]);
}
/**
* 获取报价表
*/
public function getQuotationExcel()
{
try {
$param = $this->param;
if (!isset($param['product_list']) || !$param['product_list']) {
throw new \Exception('请先选择产品');
}
if (!isset($param['discount'])) {
throw new \Exception('折扣参数错误,请刷新后重试');
}
$productList = $param['product_list'];//选中的商品列表
$discount = $param['discount'] ? $param['discount'] * 0.01 : 0;//折扣
$spreadsheet = new Spreadsheet();// 创建新的Spreadsheet对象
$spreadsheet->getActiveSheet()->mergeCells('A1:O1');// 合并单元格,这里是合并第一行的A1和B1单元格
$spreadsheet->getActiveSheet()->setCellValue('A1', '金 华 市 东 大 泵 业 有 限 公 司 报 价 数 据 表'); // 设置合并后的单元格内容
// 设置字体加粗
$styleArray = array(
'font' => array(
'bold' => true,
'color' => array('rgb' => 'FF0000'), // 红色
'size' => 15,
)
);
// 设置居中对齐
$spreadsheet->getActiveSheet()->getStyle('A1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
$spreadsheet->getActiveSheet()->getStyle('A1')->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
$spreadsheet->getActiveSheet()->getStyle('A1')->applyFromArray($styleArray);
$header = [
'index' => '序号',
'name' => '产品名称',
'specification' => '对应型号',
'money' => '单价(元/套)',
'number' => '数量(套)',
'discount' => '优惠折扣',
'sum_money' => '合计(元)',
'pump_diameter' => '泵体直径E(mm)',
'qutlet_diameter' => '出水口径(mm)',
'pump_weight' => '泵体重量(kg)',
'motor_weight' => '电机重量(kg)',
'sum_weight' => '总重量',
'pump_length' => '泵体长度(mm)',
'motor_length' => '电机长度(mm)',
'sum_length' => '总长度',
];
$columnIndex = 1;
foreach ($header as $item) {
$spreadsheet->getActiveSheet()->setCellValueByColumnAndRow($columnIndex, 2, $item);
$columnIndex++;
}
$tableData = [];//表格数据
$sumAllMoney = 0.00;
//处理数据
foreach ($productList as $key => $value) {
$tableData[] = [
'index' => $key + 1,
'name' => $value['name'],
'specification' => $value['specification'],
'money' => $value['money'] * 1,
'number' => $value['number'] * 1,
'discount' => $param['discount'] > 0 ? ($param['discount'] . '%') : '0%',
'sum_money' => $discount > 0 ? ($value['money'] * 1) * ($value['number'] * 1) * $discount : ($value['money'] * 1) * ($value['number'] * 1),
'pump_diameter' => $value['pump_diameter'],
'qutlet_diameter' => $value['qutlet_diameter'],
'pump_weight' => $value['pump_weight'],
'motor_weight' => $value['motor_weight'],
'sum_weight' => $value['pump_weight'] * 1 + $value['motor_weight'] * 1,
'pump_length' => $value['pump_length'],
'motor_length' => $value['motor_length'],
'sum_length' => $value['pump_length'] * 1 + $value['motor_length'] * 1,
];
$sumAllMoney += $discount > 0 ? number_format(($value['money'] * 1) * ($value['number'] * 1) * $discount,2) : number_format(($value['money'] * 1) * ($value['number'] * 1),2);
}
$rowIndex = 3;
foreach ($tableData as $value) {
$columnIndex = 1;
foreach ($header as $key => $val) {
$spreadsheet->getActiveSheet()->setCellValueByColumnAndRow($columnIndex, $rowIndex, $value[$key]);
$columnIndex++;
}
$rowIndex++;
}
// 设置居中对齐
$line = 2 + count($tableData);
$spreadsheet->getActiveSheet()->getStyle('A2:O' . $line)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
$spreadsheet->getActiveSheet()->getStyle('A2:O' . $line)->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
//统一设置行高
$spreadsheet->getActiveSheet()->getDefaultColumnDimension()->setWidth(15);
for ($i = 1; $i <= (2 + count($tableData)); $i++) {
$spreadsheet->getActiveSheet()->getRowDimension($i)->setRowHeight(30); // 设置第一行的高度为30
}
$fileName = time() . '东大泵业报价数据表.xlsx';
$writer = new Xlsx($spreadsheet);// 创建Excel写入器
$path = '/uploads/file/quotationExcel/' . $fileName;
$absolutePath = './' . $path;
$writer->save($absolutePath);// 保存Excel文件到服务器的一个文件中
} catch (\Exception $e) {
$this->error($e->getMessage() . $e->getLine());
}
$this->success('获取成功', ['url' => cdnurl($path), 'sumMoney' => number_format($sumAllMoney,2)]);
}
}
发表评论 取消回复