|
@@ -0,0 +1,509 @@
|
|
|
+<template>
|
|
|
+ <div v-loading="loading">
|
|
|
+ <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="formDom">
|
|
|
+ <template #chart>
|
|
|
+ <div style="width:100%;padding-left:25px">
|
|
|
+ <div ref="chartDom" style="height:300px"></div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template #buyer>
|
|
|
+ <div style="width: 100%">
|
|
|
+ <el-form-item label="客户信息" prop="buyCorporationId" class="wid100">
|
|
|
+ <el-select v-model="formData.data.buyCorporationId" filterable remote reserve-keyword placeholder="请输入关键字" remote-show-suffix
|
|
|
+ :remote-method="remoteMethod" :loading="loadingSearch" @input="remoteMethod" style="width: 100%" @change="changeCustomer">
|
|
|
+ <el-option v-for="item in customerList" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="地址" class="wid100">
|
|
|
+ <el-row style="width: 100%">
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="" prop="buyCountryId" class="margin-b-0 wid100">
|
|
|
+ <el-select v-model="formData.data.buyCountryId" placeholder="国家" style="width:100%" filterable
|
|
|
+ @change="(val) => getCityData(val, '20', true)">
|
|
|
+ <el-option v-for="item in countryData" :label="item.name" :value="item.id">
|
|
|
+ </el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="" prop="provinceName" class="margin-b-0 wid100">
|
|
|
+ <selectCity placeholder="省/洲" @change="(val) => getCityData(val, '30', true)" addressId="buyProvinceId" addressName="provinceName"
|
|
|
+ v-model="formData.data" :data="provinceData">
|
|
|
+ </selectCity>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="" prop="cityName" class="margin-b-0 wid100">
|
|
|
+ <selectCity placeholder="城市" addressId="buyCityId" addressName="cityName" v-model="formData.data" :data="cityData">
|
|
|
+ </selectCity>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-form-item label="" prop="buyPostalCode" class="margin-b-0">
|
|
|
+ <el-input v-model="formData.data.buyPostalCode" placeholder="请输入邮编" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="详细地址" prop="buyAddress" class="margin-b-0 wid100">
|
|
|
+ <el-input v-model="formData.data.buyAddress" type="textarea">
|
|
|
+ </el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template #commodity>
|
|
|
+ <div style="width: 100%;padding-left:25px">
|
|
|
+ <el-table :data="formData.data.quotationProductList" style="width: 100%;" default-expand-all>
|
|
|
+ <el-table-column type="expand" width="50" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ <div style="padding-left:50px">
|
|
|
+ <div style="margin-bottom:10px;">
|
|
|
+ <TitleInfo content='BOM单:'></TitleInfo>
|
|
|
+ </div>
|
|
|
+ <el-table :data="scope.row.quotationProductBomList" style="width: 100%;" border class="bom-table">
|
|
|
+ <el-table-column label="图片" width="80">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <div v-if="row.fileUrl">
|
|
|
+ <img :src="row.fileUrl" class="pic" @click="onPicture(row.fileUrl)" />
|
|
|
+ </div>
|
|
|
+ <div v-else></div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="productCode" label="物料编码" width="190" />
|
|
|
+ <el-table-column prop="productName" label="物料名称" min-width="200" />
|
|
|
+ <el-table-column label="尺寸 cm*cm*cm" width="150">
|
|
|
+ <template #default="{ row, $index }">
|
|
|
+ <div style="width: 100%">
|
|
|
+ {{row.productLength}} * {{row.productWidth}} * {{row.productHeight}}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="quantity" label="数量" width="110" />
|
|
|
+ <!-- <template #default="{ row, $index }">
|
|
|
+ <div style="width: 100%">
|
|
|
+ <el-form-item :prop="'quotationProductList.' + scope.$index + '.quotationProductBomList.' + $index + '.quantity'"
|
|
|
+ :rules="rules.quantity" :inline-message="true" class="margin-b-0 wid100">
|
|
|
+ <el-input-number onmousewheel="return false;" v-model="row.quantity" placeholder="请输入" style="width: 100%" :precision="0"
|
|
|
+ :controls="false" :min="1" :disabled="row.type==1" @change="changeQuantity()" />
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ </template> -->
|
|
|
+ <el-table-column prop="allQuantity" label="总量" width="80" />
|
|
|
+ <el-table-column prop="price" label="单价" width="110" />
|
|
|
+ <el-table-column prop="amount" label="小计" width="110" />
|
|
|
+ <el-table-column prop="remark" label="备注" width="180" />
|
|
|
+ <!-- <template #default="{ row, $index }">
|
|
|
+ <div style="width: 100%">
|
|
|
+ <el-form-item :prop="'quotationProductList.' + scope.$index + '.quotationProductBomList.' + $index + '.remark'"
|
|
|
+ :rules="rules.remark" :inline-message="true" class="margin-b-0 wid100">
|
|
|
+ <el-input v-model="row.remark" placeholder="请输入" style="width: 100%" :min="0" />
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ </template> -->
|
|
|
+
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="图片" width="80">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <div v-if="row.fileUrl">
|
|
|
+ <img :src="row.fileUrl" class="pic" @click="onPicture(row.fileUrl)" />
|
|
|
+ </div>
|
|
|
+ <div v-else></div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="productCode" label="商品编码" width="190" />
|
|
|
+ <el-table-column prop="productName" label="商品名称" min-width="200" />
|
|
|
+ <el-table-column label="尺寸 cm*cm*cm" width="150">
|
|
|
+ <template #default="{ row, $index }">
|
|
|
+ <div style="width: 100%">
|
|
|
+ {{row.productLength}} * {{row.productWidth}} * {{row.productHeight}}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="quantity" label="数量" width="110" />
|
|
|
+ <!-- <template #default="{ row, $index }">
|
|
|
+ <div style="width: 100%">
|
|
|
+ <el-form-item :prop="'quotationProductList.' + $index + '.quantity'" :rules="rules.quantity" :inline-message="true"
|
|
|
+ class="margin-b-0 wid100">
|
|
|
+ <el-input-number onmousewheel="return false;" v-model="row.quantity" placeholder="请输入" style="width: 100%" :precision="0"
|
|
|
+ :controls="false" :min="1" @change="changeQuantity()" />
|
|
|
+
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ </template> -->
|
|
|
+ <el-table-column prop="price" label="单价" width="110" />
|
|
|
+ <el-table-column prop="amount" label="小计" width="110" />
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </byForm>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import byForm from "@/components/byForm/index";
|
|
|
+import selectCity from "@/components/selectCity/index.vue";
|
|
|
+import * as echarts from "echarts";
|
|
|
+const { proxy } = getCurrentInstance();
|
|
|
+const props = defineProps({
|
|
|
+ rowData: Object,
|
|
|
+ dataType: {
|
|
|
+ type: String,
|
|
|
+ default: "1",
|
|
|
+ },
|
|
|
+});
|
|
|
+const typeData = ref([
|
|
|
+ {
|
|
|
+ label: "内销",
|
|
|
+ value: "1",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: "外销",
|
|
|
+ value: "2",
|
|
|
+ },
|
|
|
+]);
|
|
|
+const treeData = ref([]);
|
|
|
+const countryData = ref([]);
|
|
|
+const provinceData = ref([]);
|
|
|
+const cityData = ref([]);
|
|
|
+const formData = reactive({
|
|
|
+ data: {
|
|
|
+ type: "1",
|
|
|
+ quotationProductList: [],
|
|
|
+ },
|
|
|
+});
|
|
|
+const loading = ref(false);
|
|
|
+const formDom = ref(null);
|
|
|
+const formOption = reactive({
|
|
|
+ inline: true,
|
|
|
+ labelWidth: 100,
|
|
|
+ itemWidth: 100,
|
|
|
+ disabled: true,
|
|
|
+});
|
|
|
+let myChart = null;
|
|
|
+const chartDom = ref(null);
|
|
|
+const isShowChart = ref(false);
|
|
|
+const formConfig = computed(() => {
|
|
|
+ return [
|
|
|
+ // {
|
|
|
+ // type: "title1",
|
|
|
+ // title: "报价趋势",
|
|
|
+ // isShow: isShowChart.value,
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // type: "slot",
|
|
|
+ // slotName: "chart",
|
|
|
+ // isShow: isShowChart.value,
|
|
|
+ // },
|
|
|
+ {
|
|
|
+ type: "title1",
|
|
|
+ title: "报价类型",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: "select",
|
|
|
+ prop: "type",
|
|
|
+ label: "报价类型",
|
|
|
+ data: typeData.value,
|
|
|
+ itemWidth: 50,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: "treeSelect",
|
|
|
+ prop: "companyId",
|
|
|
+ label: "报价公司",
|
|
|
+ data: treeData.value,
|
|
|
+ propsTreeLabel: "deptName",
|
|
|
+ propsTreeValue: "deptId",
|
|
|
+ itemWidth: 50,
|
|
|
+ fn: (val) => {
|
|
|
+ companyId.value = val;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: "title1",
|
|
|
+ title: "贸易信息",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: "slot",
|
|
|
+ slotName: "buyer",
|
|
|
+ label: "",
|
|
|
+ itemWidth: 100,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: "input",
|
|
|
+ itemType: "text",
|
|
|
+ label: "联系人",
|
|
|
+ prop: "buyContactName",
|
|
|
+ itemWidth: 50,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: "input",
|
|
|
+ itemType: "text",
|
|
|
+ label: "联系人电话",
|
|
|
+ prop: "buyContactNumber",
|
|
|
+ itemWidth: 50,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: "title1",
|
|
|
+ title: "商品信息",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: "slot",
|
|
|
+ slotName: "commodity",
|
|
|
+ label: "",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: "title1",
|
|
|
+ title: "报价总金额",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: "input",
|
|
|
+ prop: "amount",
|
|
|
+ label: "报价总金额",
|
|
|
+ itemWidth: 25,
|
|
|
+ disabled: true,
|
|
|
+ },
|
|
|
+ ];
|
|
|
+});
|
|
|
+
|
|
|
+const getCityData = (id, type, isChange = false) => {
|
|
|
+ proxy.post("/customizeArea/list", { parentId: id }).then((res) => {
|
|
|
+ if (type === "20") {
|
|
|
+ provinceData.value = res;
|
|
|
+ if (isChange) {
|
|
|
+ formData.data.buyProvinceId = "";
|
|
|
+ formData.data.provinceName = "";
|
|
|
+ formData.data.buyCityId = "";
|
|
|
+ formData.data.cityName = "";
|
|
|
+ }
|
|
|
+ } else if (type === "30") {
|
|
|
+ cityData.value = res;
|
|
|
+ if (isChange) {
|
|
|
+ formData.data.buyCityId = "";
|
|
|
+ formData.data.cityName = "";
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ countryData.value = res;
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+getCityData("0");
|
|
|
+
|
|
|
+const getDict = () => {
|
|
|
+ proxy
|
|
|
+ .get("/tenantDept/list", {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 9999,
|
|
|
+ keyword: "",
|
|
|
+ tenantId: proxy.useUserStore().user.tenantId,
|
|
|
+ type: 0,
|
|
|
+ })
|
|
|
+ .then((res) => {
|
|
|
+ treeData.value = proxy.handleTree(res.data, "deptId");
|
|
|
+ });
|
|
|
+};
|
|
|
+getDict();
|
|
|
+
|
|
|
+const getFileData = () => {
|
|
|
+ let ids = [];
|
|
|
+ formData.data.quotationProductList.map((x) => {
|
|
|
+ ids.push(x.productId);
|
|
|
+ x.quotationProductBomList.map((y) => {
|
|
|
+ ids.push(y.materialId);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ ids = Array.from(new Set(ids));
|
|
|
+ proxy
|
|
|
+ .post("/fileInfo/getList", {
|
|
|
+ businessIdList: ids,
|
|
|
+ })
|
|
|
+ .then((fileObj) => {
|
|
|
+ formData.data.quotationProductList.map((x) => {
|
|
|
+ x.fileList = fileObj[x.productId] || [];
|
|
|
+ if (x.fileList && x.fileList.length > 0) {
|
|
|
+ x.fileUrl = x.fileList[0].fileUrl;
|
|
|
+ }
|
|
|
+ x.quotationProductBomList.map((y) => {
|
|
|
+ y.fileList = fileObj[y.materialId] || [];
|
|
|
+ if (y.fileList && y.fileList.length > 0) {
|
|
|
+ y.fileUrl = y.fileList[0].fileUrl;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+};
|
|
|
+const changeQuantity = () => {
|
|
|
+ let money = 0;
|
|
|
+ if (
|
|
|
+ formData.data.quotationProductList &&
|
|
|
+ formData.data.quotationProductList.length > 0
|
|
|
+ ) {
|
|
|
+ // 单个产品的价格
|
|
|
+ for (let i = 0; i < formData.data.quotationProductList.length; i++) {
|
|
|
+ let iele = formData.data.quotationProductList[i];
|
|
|
+ let productPrice = 0;
|
|
|
+ for (let j = 0; j < iele.quotationProductBomList.length; j++) {
|
|
|
+ const jele = iele.quotationProductBomList[j];
|
|
|
+ productPrice += Number(
|
|
|
+ parseFloat(Number(jele.quantity) * Number(jele.price)).toFixed(2)
|
|
|
+ );
|
|
|
+ }
|
|
|
+ iele.price = parseFloat(productPrice).toFixed(2);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算数量以及小计
|
|
|
+ for (let i = 0; i < formData.data.quotationProductList.length; i++) {
|
|
|
+ let iele = formData.data.quotationProductList[i];
|
|
|
+ if (iele.quantity) {
|
|
|
+ if (iele.price) {
|
|
|
+ iele.amount = parseFloat(
|
|
|
+ Number(iele.quantity) * Number(iele.price)
|
|
|
+ ).toFixed(2);
|
|
|
+ money += Number(iele.amount);
|
|
|
+ }
|
|
|
+ for (let j = 0; j < iele.quotationProductBomList.length; j++) {
|
|
|
+ const jele = iele.quotationProductBomList[j];
|
|
|
+ jele.allQuantity = iele.quantity * jele.quantity;
|
|
|
+ if (jele.price) {
|
|
|
+ jele.amount = parseFloat(
|
|
|
+ Number(jele.allQuantity) * Number(jele.price)
|
|
|
+ ).toFixed(2);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ formData.data.amount = parseFloat(money).toFixed(2);
|
|
|
+ }
|
|
|
+};
|
|
|
+const chartData = ref([]);
|
|
|
+const chartOption = reactive({
|
|
|
+ data: {
|
|
|
+ tooltip: {
|
|
|
+ trigger: "axis",
|
|
|
+ },
|
|
|
+ // legend: {
|
|
|
+ // data: ["价格"],
|
|
|
+ // },
|
|
|
+ grid: {
|
|
|
+ left: "3%",
|
|
|
+ right: "6%",
|
|
|
+ top: "10%",
|
|
|
+ bottom: "3%",
|
|
|
+ containLabel: true,
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ show: true,
|
|
|
+ trigger: "axis",
|
|
|
+ // 格式化函数
|
|
|
+ // valueFormatter: (val) => {
|
|
|
+ // return val + "aaa";
|
|
|
+ // },
|
|
|
+ formatter: (params, ticket, callback) => {
|
|
|
+ return `
|
|
|
+ ${params[0].seriesName}:${params[0].value}
|
|
|
+ <br/>
|
|
|
+ 报价单号:${chartData.value[params[0].dataIndex].code}
|
|
|
+ `;
|
|
|
+ },
|
|
|
+ textStyle: {
|
|
|
+ fontSize: 12,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ // toolbox: {
|
|
|
+ // feature: {
|
|
|
+ // saveAsImage: {},
|
|
|
+ // },
|
|
|
+ // },
|
|
|
+ xAxis: {
|
|
|
+ type: "category",
|
|
|
+ boundaryGap: false,
|
|
|
+ data: [],
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: "value",
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: "报价金额",
|
|
|
+ type: "line",
|
|
|
+ data: [],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+});
|
|
|
+
|
|
|
+const getData = (query) => {
|
|
|
+ let url =
|
|
|
+ props.dataType == "1" ? "/saleQuotation/detail" : "/extQuotation/detail";
|
|
|
+ loading.value = true;
|
|
|
+ proxy.post(url, query).then((res) => {
|
|
|
+ formData.data = res;
|
|
|
+ // 城市数据回显
|
|
|
+ if (formData.data.buyCountryId) {
|
|
|
+ getCityData(formData.data.buyCountryId, "20");
|
|
|
+ }
|
|
|
+ if (formData.data.buyProvinceId) {
|
|
|
+ getCityData(formData.data.buyProvinceId, "30");
|
|
|
+ }
|
|
|
+ getFileData();
|
|
|
+ changeQuantity();
|
|
|
+ loading.value = false;
|
|
|
+ // if (res.quotationTrendList && res.quotationTrendList.length >= 2) {
|
|
|
+ // isShowChart.value = true;
|
|
|
+ // nextTick(() => {
|
|
|
+ // myChart = echarts.init(chartDom.value);
|
|
|
+ // window.addEventListener("resize", () => {
|
|
|
+ // myChart.resize();
|
|
|
+ // });
|
|
|
+ // chartData.value = res.quotationTrendList;
|
|
|
+ // chartOption.data.xAxis.data = chartData.value.map((item) => {
|
|
|
+ // return item.createTime.slice(0, 10);
|
|
|
+ // });
|
|
|
+ // chartOption.data.series[0].data = chartData.value.map((item, index) => {
|
|
|
+ // if (item.code == props.rowData.code) {
|
|
|
+ // return {
|
|
|
+ // value: item.amount || 0,
|
|
|
+ // itemStyle: { color: "red" },
|
|
|
+ // };
|
|
|
+ // } else {
|
|
|
+ // return item.amount || 0;
|
|
|
+ // }
|
|
|
+ // });
|
|
|
+ // myChart.setOption(chartOption.data);
|
|
|
+ // myChart.resize();
|
|
|
+ // });
|
|
|
+ // } else {
|
|
|
+ // isShowChart.value = false;
|
|
|
+ // }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+watch(
|
|
|
+ () => props.rowData,
|
|
|
+ (val) => {
|
|
|
+ if (props.rowData.id) {
|
|
|
+ getData({ id: props.rowData.id });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ immediate: true,
|
|
|
+ deep: true,
|
|
|
+ }
|
|
|
+);
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.pic {
|
|
|
+ object-fit: contain;
|
|
|
+ width: 50px;
|
|
|
+ height: 50px;
|
|
|
+ cursor: pointer;
|
|
|
+ vertical-align: middle;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.bom-table .el-table__body-wrapper .el-table__body .el-table__row) {
|
|
|
+ background: #fbfbfb !important;
|
|
|
+}
|
|
|
+</style>
|