This commit is contained in:
Xulinchuan 2025-06-13 09:44:58 +08:00
commit 3e7f25d00b
10 changed files with 518 additions and 41 deletions

View File

@ -190,7 +190,7 @@ const props = defineProps({
// //
headerCellClassName: { headerCellClassName: {
type: Function, type: Function,
default: () => "", default: () => "custom-header",
}, },
// //
cellClassName: { cellClassName: {

View File

@ -311,6 +311,18 @@ export const constantRoutes = [
title: "电商交易", title: "电商交易",
}, },
children: [ children: [
{
path: 'trade/dataBoard',
component: () => import('@/views/trade/dataBoard'),
name: 'dataBoard',
hidden: false,
meta: {
title: "电商数据看板",
icon: "",
noCache: false,
link: null,
},
},
{ {
path: 'trade/agriculturalinputstrading', path: 'trade/agriculturalinputstrading',
component: () => import('@/views/trade/agriculturalinputstrading'), component: () => import('@/views/trade/agriculturalinputstrading'),
@ -361,7 +373,7 @@ export const constantRoutes = [
}, },
], ],
}, },
{ {
path: '/brand', path: '/brand',
name: "brand", name: "brand",
component: Layout, component: Layout,

View File

@ -233,7 +233,7 @@ const generateMockData = () => {
"id|+1": 10000, "id|+1": 10000,
nickname: () => generateFoodNickname(), // nickname: () => generateFoodNickname(), //
userName: "@cname", // userName: "@cname", //
accountNumber: Mock.mock("@id").toString().slice(0, 10), // accountNumber: "@integer(1000000000, 9999999999)", //
phoneNumber: "@integer(13000000000, 18999999999)", // phoneNumber: "@integer(13000000000, 18999999999)", //
sex: '@pick(["男", "女"])', // sex: '@pick(["男", "女"])', //
"userStatus|1": [0, 1], //0 1 "userStatus|1": [0, 1], //0 1

View File

@ -218,7 +218,7 @@ const generateMockData = () => {
"id|+1": 10000, "id|+1": 10000,
nickname: () => generateFoodNickname(), // nickname: () => generateFoodNickname(), //
userName: "@cname", // userName: "@cname", //
accountNumber: Mock.mock("@id").toString().slice(0, 10), // accountNumber: "@integer(1000000000, 9999999999)", //
phoneNumber: "@integer(13000000000, 18999999999)", // phoneNumber: "@integer(13000000000, 18999999999)", //
sex: '@pick(["男", "女"])', // sex: '@pick(["男", "女"])', //
"userStatus|1": [0, 1], //0 1 "userStatus|1": [0, 1], //0 1

View File

@ -147,7 +147,6 @@ const onSubmit = () => {
ElMessage.warning("请选择开始日期!"); ElMessage.warning("请选择开始日期!");
return; return;
} }
console.log(formInline);
loadData(); loadData();
}; };
const resetForm = () => { const resetForm = () => {
@ -268,12 +267,24 @@ const enableAndDisable = async (row, num) => {
tableLoading.value = false; tableLoading.value = false;
} }
}; };
// //
const handleEdit = (row) => { const addCategory = () => {
nowClickRow.value = row; dialogTitle.value = "添加分类";
console.log("要编辑的行:", row); getGoodTypeList();
dialogTitle.value = "编辑分类"; Object.assign(dialogForm, { //
id: "",
type: 1,
name: "",
level: "",
parentId: "",
status: "1",
});
dialogFormVisible.value = true; dialogFormVisible.value = true;
};
//
const handleEdit = async (row) => {
nowClickRow.value = row;
dialogTitle.value = "编辑分类";
getGoodTypeList(); getGoodTypeList();
dialogForm.id = row.id; dialogForm.id = row.id;
dialogForm.type = row.type; dialogForm.type = row.type;
@ -281,15 +292,16 @@ const handleEdit = (row) => {
dialogForm.level = row.level; dialogForm.level = row.level;
dialogForm.parentId = row.parentId; dialogForm.parentId = row.parentId;
dialogForm.status = row.status?.toString() ?? '1'; dialogForm.status = row.status?.toString() ?? '1';
dialogForm.selectedNode = findNodeById(goodsOptions.value, row.parentId);
dialogFormVisible.value = true;
}; };
// //
const handleDelete = (row) => { const handleDelete = (row) => {
console.log("要删除的行:", row);
deleteGoods(row.id); deleteGoods(row.id);
}; };
// //
const batchDelete = () => { const batchDelete = () => {
console.log("要删除的行:", selectedIds.value);
deleteGoods(selectedIds.value.join(",")); deleteGoods(selectedIds.value.join(","));
}; };
const deleteGoods = async (ids) => { const deleteGoods = async (ids) => {
@ -371,22 +383,22 @@ const onSaveCategory = () => {
try { try {
proxy.$modal.loading("正在保存..."); proxy.$modal.loading("正在保存...");
let param = { ...dialogForm }; let param = { ...dialogForm };
param.level = dialogForm.selectedNode.level; param.level = dialogForm.selectedNode.level ?? "";
param.type = dialogForm.selectedNode.type; param.type = dialogForm.selectedNode.type ?? "";
let response; let response;
if (dialogTitle.value == "编辑分类") { if (dialogTitle.value == "添加分类") {
response = await categoryEdit(param);
} else {
response = await categoryAdd(param); response = await categoryAdd(param);
} else {
response = await categoryEdit(param);
} }
proxy.$modal.closeLoading(); proxy.$modal.closeLoading();
if (response.code == 200) { if (response.code == 200) {
cancelDialog(); cancelDialog();
onSubmit(); onSubmit();
if (dialogTitle.value == "编辑分类") { if (dialogTitle.value == "添加分类") {
ElMessage.success("编辑成功!");
} else {
ElMessage.success("添加成功!"); ElMessage.success("添加成功!");
} else {
ElMessage.success("编辑成功!");
} }
} else { } else {
ElMessage.error(response.msg); ElMessage.error(response.msg);
@ -397,20 +409,6 @@ const onSaveCategory = () => {
} }
}); });
}; };
//
const addCategory = () => {
dialogTitle.value = "添加分类";
getGoodTypeList();
Object.assign(dialogForm, { //
id: "",
type: 1,
name: "",
level: "",
parentId: "",
status: "1",
});
dialogFormVisible.value = true;
};
const cancelDialog = () => { const cancelDialog = () => {
Object.assign(dialogForm, { // Object.assign(dialogForm, { //
id: "", id: "",

View File

@ -54,7 +54,7 @@
</el-form-item> </el-form-item>
<div v-if="formInline.specStyle == '1'"> <div v-if="formInline.specStyle == '1'">
<el-form-item label="规格名称" prop="goodSpecs" required> <el-form-item label="规格名称" prop="goodSpecs" required>
<el-input v-model="formInline.goodSpecs" class="attr-clomn" placeholder="请输入规格名称" /> <el-input v-model="formInline.goodSpecs" clearable class="attr-clomn" placeholder="请输入规格名称" />
</el-form-item> </el-form-item>
<!-- 销售价格 --> <!-- 销售价格 -->
<el-form-item label="销售价格" prop="salePrice" required> <el-form-item label="销售价格" prop="salePrice" required>
@ -219,6 +219,9 @@ import { getRegion } from "@/api/common";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import areaList from "./areaList"; import areaList from "./areaList";
const route = useRoute()
const router = useRouter()
const formRef = ref(null) const formRef = ref(null)
const formInline = reactive({ const formInline = reactive({
goodName: "", goodName: "",
@ -232,6 +235,7 @@ const formInline = reactive({
traceCode: "", // traceCode: "", //
specStyle: "1",// 1 2 specStyle: "1",// 1 2
goodSpecs: "",
salePrice: 1,//- salePrice: 1,//-
stock: 1,//- stock: 1,//-
netContent: 1,//- netContent: 1,//-
@ -324,6 +328,24 @@ const rules = reactive({
specStyle: [ specStyle: [
{ required: true, message: '请选择规格样式', trigger: 'change' } { required: true, message: '请选择规格样式', trigger: 'change' }
], ],
goodSpecs: formInline.specStyle == '1' ? [
{
required: true,
message: '请输入规格名称',
trigger: ['blur', 'change']
},
{
validator: (rule, value, callback) => {
console.log(value);
if (value == "" || value.trim() === "" ) {
callback(new Error('请输入规格名称'))
} else {
callback()
}
},
trigger: ['blur', 'change']
},
] : [],
salePrice: formInline.specStyle == '1' ? [ salePrice: formInline.specStyle == '1' ? [
{ {
validator: (rule, value, callback) => { validator: (rule, value, callback) => {

View File

@ -235,6 +235,7 @@ const formInline = reactive({
traceCode: "", // traceCode: "", //
specStyle: "1",// 1 2 specStyle: "1",// 1 2
goodSpecs: "",
salePrice: 1,//- salePrice: 1,//-
stock: 1,//- stock: 1,//-
netContent: 1,//- netContent: 1,//-
@ -327,6 +328,24 @@ const rules = reactive({
specStyle: [ specStyle: [
{ required: true, message: '请选择规格样式', trigger: 'change' } { required: true, message: '请选择规格样式', trigger: 'change' }
], ],
goodSpecs: formInline.specStyle == '1' ? [
{
required: true,
message: '请输入规格名称',
trigger: ['blur', 'change']
},
{
validator: (rule, value, callback) => {
console.log(value);
if (value == "" || value.trim() === "" ) {
callback(new Error('请输入规格名称'))
} else {
callback()
}
},
trigger: ['blur', 'change']
},
] : [],
salePrice: formInline.specStyle == '1' ? [ salePrice: formInline.specStyle == '1' ? [
{ {
validator: (rule, value, callback) => { validator: (rule, value, callback) => {

View File

@ -43,7 +43,7 @@
<el-form-item label="商品图片" prop="goodUrl" required> <el-form-item label="商品图片" prop="goodUrl" required>
<div> <div>
<!-- <myUploadImage v-model="formInline.goodUrl"></myUploadImage> --> <!-- <myUploadImage v-model="formInline.goodUrl"></myUploadImage> -->
<img style="width: 120px;" :src="formInline.goodUrl" alt=""> <img style="width: 120px;" v-for="(src,index) in goodurls" :key="index" :src="src" alt="">
</div> </div>
</el-form-item> </el-form-item>
<!-- 规格样式 --> <!-- 规格样式 -->
@ -88,9 +88,9 @@
<el-form-item label="商品规格" prop="netWeight" required> <el-form-item label="商品规格" prop="netWeight" required>
<div v-for="(item, index) in formInline.netWeight" :key="index" class="attr-item"> <div v-for="(item, index) in formInline.netWeight" :key="index" class="attr-item">
<el-input v-model="item.goodSpecs" :value="item.goodSpecs" class="attr-clomn" placeholder="请输入规格名称" /> <el-input v-model="item.goodSpecs" :value="item.goodSpecs" class="attr-clomn" placeholder="请输入规格名称" />
<el-icon size="20px" v-if="index !== 0" @click="deleteSpecs(index)" style="cursor: pointer;"> <!-- <el-icon size="20px" v-if="index !== 0" @click="deleteSpecs(index)" style="cursor: pointer;">
<Delete /> <Delete />
</el-icon> </el-icon> -->
</div> </div>
<!-- <el-button icon="plus" type="primary" plain style="margin: 0" @click="addSpecs">添加规格</el-button> --> <!-- <el-button icon="plus" type="primary" plain style="margin: 0" @click="addSpecs">添加规格</el-button> -->
</el-form-item> </el-form-item>
@ -223,6 +223,7 @@ import { getRegion } from "@/api/common";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import areaList from "./areaList"; import areaList from "./areaList";
import useOperateStore from "@/store/modules/operate"; import useOperateStore from "@/store/modules/operate";
import { computed } from "vue";
const operateStore = useOperateStore(); const operateStore = useOperateStore();
const route = useRoute() const route = useRoute()
@ -426,6 +427,7 @@ const rules = reactive({
{ required: true, message: '请输入详细地址', trigger: 'blur' } { required: true, message: '请输入详细地址', trigger: 'blur' }
], ],
}) })
const addSpecs = () => { const addSpecs = () => {
formInline.netWeight.push({ formInline.netWeight.push({
goodSpecs: "", goodSpecs: "",
@ -613,15 +615,19 @@ const getGoodsInfo = async (goodId) => {
} }
}; };
let goodurls = computed(() => {
return formInline.goodUrl.split(",")
})
watch(() => route.query, (newVal) => { watch(() => route.query, (newVal) => {
console.log("watch商品id:", newVal.goodId); // console.log("watchid:", newVal.goodId);
if (newVal.goodId) { if (newVal.goodId) {
getGoodsInfo(newVal.goodId); getGoodsInfo(newVal.goodId);
} }
}) })
onMounted(async () => { onMounted(async () => {
const { edit, goodId } = route.query; const { edit, goodId } = route.query;
console.log("onMounted商品id:", goodId); // console.log("onMountedid:", goodId);
getActiveInfo(); getActiveInfo();
await getGoodTypeList(); await getGoodTypeList();
getArea(); getArea();

View File

@ -43,7 +43,7 @@
<el-form-item label="商品图片" prop="goodUrl" required> <el-form-item label="商品图片" prop="goodUrl" required>
<div> <div>
<!-- <myUploadImage v-model="formInline.goodUrl"></myUploadImage> --> <!-- <myUploadImage v-model="formInline.goodUrl"></myUploadImage> -->
<img style="width: 120px;" :src="formInline.goodUrl" alt=""> <img style="width: 120px;" v-for="(src,index) in goodurls" :key="index" :src="src" alt="">
</div> </div>
</el-form-item> </el-form-item>
<!-- 规格样式 --> <!-- 规格样式 -->
@ -653,6 +653,10 @@ const getGoodsInfo = async (goodId) => {
} }
}; };
let goodurls = computed(() => {
return formInline.goodUrl.split(",")
})
watch(() => route.query, (newVal) => { watch(() => route.query, (newVal) => {
console.log("watch商品id:", newVal.goodId); console.log("watch商品id:", newVal.goodId);
if (newVal.goodId) { if (newVal.goodId) {

View File

@ -0,0 +1,416 @@
<template>
<div class="app-container">
<div class="app-container-data">
<div class="app-container-title">运营看板</div>
<div style="display: flex">
<div class="app-container-data-left">
<div class="app-container-data-left-top">
<div>
<div class="title">
<img
class="title-image"
src="../../assets/images/profile.jpg"
alt=""
/>
<div>销售总额</div>
</div>
<div class="number">12313</div>
</div>
<div>
<div class="title">
<img
class="title-image"
src="../../assets/images/profile.jpg"
alt=""
/>
<div>订单总数</div>
</div>
<div class="number">55</div>
</div>
</div>
<div class="app-container-data-left-bottom">
<div>
<div class="title">
<img
class="title-image"
src="../../assets/images/profile.jpg"
alt=""
/>
<div>浏览量</div>
</div>
<div class="number">13415</div>
</div>
<div>
<div class="title">
<img
class="title-image"
src="../../assets/images/profile.jpg"
alt=""
/>
<div>成功退款金额</div>
</div>
<div class="number">1565465</div>
</div>
</div>
</div>
<div class="app-container-data-right">
<div ref="chartRef" style="width: 600px; height: 400px"></div>
</div>
</div>
</div>
<div class="app-container-list">
<div class="app-container-block">
<div class="app-container-block-title">销售总额</div>
<div class="app-container-block-price">
15698<text style="font-size: 10px; color: #999999; margin-left: 10px"
></text
>
</div>
<div class="app-container-block-proportion">
环比<text style="color: red; font-size: 12px">2.6%</text>
</div>
</div>
<div class="app-container-block">
<div class="app-container-block-title">订单总数</div>
<div class="app-container-block-price">
52<text style="font-size: 10px; color: #999999; margin-left: 10px"
></text
>
</div>
<div class="app-container-block-proportion">
环比<text style="color: red; font-size: 12px">2.6%</text>
</div>
</div>
<div class="app-container-block">
<div class="app-container-block-title">支付转化率</div>
<div class="app-container-block-price">
16.56<text style="font-size: 10px; color: #999999; margin-left: 10px"
>%</text
>
</div>
<div class="app-container-block-proportion">
环比<text style="color: red; font-size: 12px">2.6%</text>
</div>
</div>
<div class="app-container-block">
<div class="app-container-block-title">客单价</div>
<div class="app-container-block-price">
12.33<text style="font-size: 10px; color: #999999; margin-left: 10px"
></text
>
</div>
<div class="app-container-block-proportion">
环比<text style="color: red; font-size: 12px">2.6%</text>
</div>
</div>
<div class="app-container-block">
<div class="app-container-block-title">成功退款金额</div>
<div class="app-container-block-price">
152.5<text style="font-size: 10px; color: #999999; margin-left: 10px"
></text
>
</div>
<div class="app-container-block-proportion">
环比<text style="color: red; font-size: 12px">2.6%</text>
</div>
</div>
<div class="app-container-block">
<div class="app-container-block-title">浏览量</div>
<div class="app-container-block-price">
156988<text style="font-size: 10px; color: #999999; margin-left: 10px"
></text
>
</div>
<div class="app-container-block-proportion">
环比<text style="color: red; font-size: 12px">2.6%</text>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref } from "vue";
import * as echarts from "echarts";
const chartRef = ref(null);
onMounted(() => {
if (chartRef.value) {
const myChart = echarts.init(chartRef.value);
const option = {
backgroundColor: "#fff",
title: {
text: "渐变折线图",
left: "12%",
top: "13%",
textStyle: {
color: "#999",
fontSize: 14,
},
},
legend: {
show: true,
icon: "circle",
top: "13%",
itemWidth: 6,
itemHeight: 6,
itemGap: 25,
},
tooltip: {
trigger: "axis",
},
xAxis: [
{
type: "category",
data: ["A", "B", "C", "D", "E", "F", "G"],
axisLine: {
lineStyle: {
color: "#ddd",
},
},
axisTick: {
show: false,
},
axisLabel: {
interval: 0,
textStyle: {
color: "#c56790",
},
margin: 15,
},
boundaryGap: false,
},
],
yAxis: [
{
type: "value",
axisTick: {
show: false,
},
axisLine: {
lineStyle: {
color: "#ddd",
},
},
axisLabel: {
textStyle: {
color: "#c56790",
},
},
splitLine: {
show: false,
},
},
],
series: [
{
name: "S",
type: "line",
data: [13, 10, 3, 12, 15, 30, 7],
symbolSize: 6,
symbol: "circle",
smooth: true,
lineStyle: {
color: "#fe9a8b",
},
itemStyle: {
normal: {
color: "#fe9a8b",
borderColor: "#fe9a8b",
},
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: "#fe9a8bb3",
},
{
offset: 1,
color: "#fe9a8b03",
},
]),
},
emphasis: {
itemStyle: {
color: {
type: "radial",
x: 0.5,
y: 0.5,
r: 0.5,
colorStops: [
{
offset: 0,
color: "#fe9a8b",
},
{
offset: 0.4,
color: "#fe9a8b",
},
{
offset: 0.5,
color: "#fff",
},
{
offset: 0.7,
color: "#fff",
},
{
offset: 0.8,
color: "#fff",
},
{
offset: 1,
color: "#fff",
},
],
},
borderColor: "#fe9a8b",
borderWidth: 2,
},
},
},
{
name: "M",
type: "line",
data: [5, 12, 11, 4, 25, 16, 1],
symbolSize: 6,
symbol: "circle",
smooth: true,
lineStyle: {
color: "#9E87FF",
},
itemStyle: {
normal: {
color: "#9E87FF",
borderColor: "#9E87FF",
},
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: "#9E87FFb3",
},
{
offset: 1,
color: "#9E87FF03",
},
]),
},
emphasis: {
itemStyle: {
color: {
type: "radial",
x: 0.5,
y: 0.5,
r: 0.5,
colorStops: [
{
offset: 0,
color: "#9E87FF",
},
{
offset: 0.4,
color: "#9E87FF",
},
{
offset: 0.5,
color: "#fff",
},
{
offset: 0.7,
color: "#fff",
},
{
offset: 0.8,
color: "#fff",
},
{
offset: 1,
color: "#fff",
},
],
},
borderColor: "#9E87FF",
borderWidth: 2,
},
},
},
],
};
myChart.setOption(option);
}
});
</script>
<style lang="scss" scoped>
.title {
width: 200px;
display: flex;
align-items: center;
margin-right: 50px;
font-size: 20px;
color: #999999;
}
.title-image {
margin-right: 10px;
width: 38px;
height: 38px;
}
.number {
margin-right: 100px;
margin-top: 20px;
font-size: 25px;
font-weight: bold;
color: #000000;
}
.app-container-list {
margin-top: 20px;
display: flex;
align-items: center;
justify-content: flex-start;
}
.app-container-title {
margin-bottom: 20px;
font-size: 20px;
font-weight: bold;
}
.app-container-data {
border-radius: 10px;
padding: 20px;
width: 100%;
background-color: #fff;
.app-container-data-left {
width: 40%;
.app-container-data-left-top {
display: flex;
}
.app-container-data-left-bottom {
margin-top: 50px;
display: flex;
}
}
.app-container-data-right {
width: 60%;
}
}
.app-container-block {
width: 250px;
background-color: #fff;
border-radius: 10px;
padding: 10px;
margin-left: 26px;
.app-container-block-title {
font-size: 18px;
font-weight: bold;
}
.app-container-block-price {
font-size: 18px;
color: #42b983;
font-weight: bold;
margin: 10px 0;
}
.app-container-block-proportion {
font-size: 12px;
}
}
</style>