Преглед изворни кода

销售信息反馈,新增和详情。

jiaxiaoqiang пре 8 месеци
родитељ
комит
1f9939767b

+ 1 - 1
index.html

@@ -2,7 +2,7 @@
 <html lang="en">
   <head>
     <meta charset="UTF-8" />
-    <link rel="icon" href="/public/logo.jpg" />
+    <link rel="icon" href="/logo.jpg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <meta
       name="description"

+ 37 - 0
src/api/sales/index.ts

@@ -0,0 +1,37 @@
+import request from "@/utils/request";
+
+export function getSalesData(data: object) {
+  return request({
+    url: "/api/v1/sales/page",
+    method: "post",
+    data,
+  });
+}
+
+// 获取订单列表
+export function getOrderList(data: object) {
+  return request({
+    url: "/api/v1/sales/orderPage",
+    method: "post",
+    data,
+  });
+}
+
+// 用户列表
+export function getUserList(data: object) {
+  return request({
+    url: "/api/v1/sales/queryUserList",
+    method: "get",
+    data,
+  });
+}
+
+// 新增销售反馈表
+
+export function addFeedback(data: object) {
+  return request({
+    url: "/api/v1/sales/add",
+    method: "post",
+    data,
+  });
+}

+ 9 - 0
src/router/modules/analysis.ts

@@ -57,5 +57,14 @@ export default {
         },
       ],
     },
+    {
+      path: "sales",
+      component: () => import("@/views/sales/index.vue"),
+      name: "Sales",
+      meta: {
+        title: "销售反馈",
+        icon: "Guide",
+      },
+    },
   ],
 };

+ 229 - 0
src/views/sales/add.vue

@@ -0,0 +1,229 @@
+<template>
+  <div>
+    <el-drawer v-model="drawerVisible" size="80%" :close-on-click-modal="false">
+      <template #header>
+        <h4>新增销售反馈信息</h4>
+      </template>
+      <template #default>
+        <el-form
+          ref="baseInfoRef"
+          :model="saleForm"
+          label-width="120"
+          :rules="rules"
+          style="max-width: 600px"
+        >
+          <el-form-item label="顾客名称" prop="customer">
+            <el-input v-model="saleForm.customer" />
+          </el-form-item>
+          <el-form-item label="详细地址" prop="address">
+            <el-input v-model="saleForm.address" />
+          </el-form-item>
+          <el-form-item label="联系人" prop="contacts">
+            <el-input v-model="saleForm.contacts" />
+          </el-form-item>
+
+          <el-form-item label="联系电话" prop="phoneNo">
+            <el-input v-model="saleForm.phoneNo" />
+          </el-form-item>
+          <el-form-item label="订单信息">
+            <el-select
+              v-model="selectOrderCode"
+              value-key="orderCode"
+              placeholder="请选择订单信息"
+              style="width: 100%"
+              @change="orderSelect"
+            >
+              <el-option
+                v-for="item in orders"
+                :key="item.orderCode"
+                :label="item.orderName"
+                :value="item"
+              />
+            </el-select>
+          </el-form-item>
+        </el-form>
+
+        <el-form
+          ref="remark1Ref"
+          :model="remark1"
+          label-width="120"
+          :rules="rules1"
+          style="max-width: 600px"
+        >
+          <el-form-item label="顾客投诉或建议" prop="content">
+            <el-input v-model="remark1.content" type="textarea" />
+          </el-form-item>
+          <el-form-item label="处理人" prop="contacts">
+            <el-select
+              v-model="remark1.user"
+              placeholder="请选择联系人"
+              style="width: 100%"
+            >
+              <el-option
+                v-for="item in userList"
+                :key="item.userName"
+                :label="item.userName"
+                :value="item.userName"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="日期" prop="time">
+            <el-date-picker
+              v-model="remark1.time"
+              type="date"
+              placeholder="请选择日期"
+              clearable
+              format="YYYY年MM月DD日"
+              style="width: 100%"
+            />
+          </el-form-item>
+        </el-form>
+      </template>
+      <template #footer>
+        <div class="drawer-bottom">
+          <el-button @click="cancelClick">取消</el-button>
+          <el-button type="primary" @click="confirmClick">提交</el-button>
+        </div>
+      </template>
+    </el-drawer>
+  </div>
+</template>
+
+<script setup>
+import { addFeedback, getOrderList, getUserList } from "@/api/sales/index";
+
+const drawerVisible = ref(false);
+
+const baseInfoRef = ref(null);
+const saleForm = reactive({
+  customer: "",
+  address: "",
+  contacts: "",
+  phoneNo: "",
+});
+// 顾客投诉或建议
+const remark1Ref = ref(null);
+
+const remark1 = reactive({
+  content: "",
+  user: "",
+  time: "",
+});
+
+const rules = {
+  customer: [
+    { required: true, message: "请输入顾客名称", trigger: "blur" },
+    { min: 2, max: 50, message: "长度在 2 到 50 个字符", trigger: "blur" },
+  ],
+  address: [
+    { required: true, message: "请输入详细地址", trigger: "blur" },
+    { min: 2, max: 200, message: "长度在 2 到 200 个字符", trigger: "blur" },
+  ],
+  contacts: [
+    { required: true, message: "请输入联系人", trigger: "blur" },
+    { min: 2, max: 20, message: "长度在 2 到 20 个字符", trigger: "blur" },
+  ],
+  phoneNo: [
+    { required: true, message: "请输入联系电话", trigger: "blur" },
+    { min: 11, max: 11, message: "请输入正确的手机号", trigger: "blur" },
+  ],
+};
+const rules1 = {
+  content: [
+    { required: true, message: "请输入顾客投诉或建议", trigger: "blur" },
+    { min: 2, max: 200, message: "长度在 2 到 200 个字符", trigger: "blur" },
+  ],
+  user: [
+    { required: true, message: "请输入姓名", trigger: "blur" },
+    { min: 2, max: 20, message: "长度在 2 到 20 个字符", trigger: "blur" },
+  ],
+  time: [{ required: true, message: "请选择日期", trigger: "change" }],
+};
+
+// 订单相关信息
+const orders = ref([]);
+const selectedOrder = ref({});
+const selectOrderCode = ref("");
+const getOrders = async () => {
+  const res = await getOrderList({
+    pageNo: 0,
+    pageSize: 200,
+  });
+  orders.value = res.data.records;
+  if (orders.value.length > 0) {
+    // selectOrderCode.value = orders.value[0].orderCode;
+
+    selectedOrder.value = orders.value[0];
+  }
+};
+const orderSelect = (value) => {
+  selectedOrder.value = value;
+};
+
+// 用户信息
+const userInfo = ref({});
+const userList = ref([]);
+const getUserInfo = async () => {
+  const res = await getUserList();
+  userList.value = res.data;
+  // if (userList.value.length > 0) {
+  //   userInfo.value = userList.value[0];
+  // }
+};
+
+const showDrawer = () => {
+  getOrders();
+  getUserInfo();
+  drawerVisible.value = true;
+};
+
+defineExpose({ showDrawer });
+
+const cancelClick = () => {
+  drawerVisible.value = false;
+  baseInfoRef.value.resetFields();
+  remark1Ref.value.resetFields();
+};
+
+const confirmClick = () => {
+  console.log(saleForm);
+  console.log(remark1);
+  console.log(selectedOrder.value);
+
+  // drawerVisible.value = false;
+  //
+  // // 验证表单
+  baseInfoRef.value.validate((valid) => {
+    if (valid) {
+      console.log("baseInfoRef valid");
+    } else {
+      console.log("baseInfoRef invalid");
+      return false;
+    }
+  });
+  remark1Ref.value.validate((valid) => {
+    if (valid) {
+      let p = {
+        ...saleForm,
+        ...selectedOrder.value,
+        remark1: JSON.stringify(remark1),
+      };
+
+      addFeedback(p).then(() => {
+        cancelClick();
+      });
+    } else {
+      console.log("remark1Ref invalid");
+      return false;
+    }
+  });
+};
+</script>
+
+<style scoped lang="scss">
+.drawer-bottom {
+  display: flex;
+  justify-content: flex-start;
+  align-items: center;
+}
+</style>

+ 138 - 0
src/views/sales/detail.vue

@@ -0,0 +1,138 @@
+<template>
+  <el-drawer v-model="drawerVisible" size="1000">
+    <template #header>
+      <div><el-button type="primary" v-print="'#print'">打印</el-button></div>
+    </template>
+
+    <div id="print">
+      <div class="sale-header">销售信息反馈表</div>
+      <table border-collapse="collapse">
+        <thead>
+          <tr>
+            <th>顾客名称</th>
+            <th>详细地址</th>
+            <th>联系人</th>
+            <th>联系电话</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr>
+            <td>{{ saleModel.customer }}</td>
+            <td>{{ saleModel.address }}</td>
+            <td>{{ saleModel.contacts }}</td>
+            <td>{{ saleModel.phoneNo }}</td>
+          </tr>
+          <tr v-for="(item, index) in contentArray" :key="index">
+            <td colspan="4">
+              <div class="sale-box">
+                <div class="sale-title">{{ item.title }}</div>
+                <div class="sale-content">
+                  {{ item.content }}
+                </div>
+                <div class="sale-bottom">
+                  <div class="desc">处理人:{{ item.user }}</div>
+                  <div class="desc">时间: {{ item.time }}</div>
+                </div>
+              </div>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+  </el-drawer>
+</template>
+
+<script setup>
+const saleModel = ref({});
+const drawerVisible = ref(false);
+
+const contentArray = ref([]);
+
+const showDrawer = (row) => {
+  saleModel.value = row;
+  contentArray.value = [];
+
+  if (row.remark1) {
+    let remark1 = JSON.parse(row.remark1);
+    contentArray.value.push({ ...remark1, title: "顾客投诉或建议" });
+  }
+  if (row.remark2) {
+    let remark2 = JSON.parse(row.remark2);
+    contentArray.value.push({ ...remark2, title: "投诉或建议处置意见" });
+  }
+  if (row.remark3) {
+    let remark3 = JSON.parse(row.remark3);
+    contentArray.value.push({ ...remark3, title: "原因分析" });
+  }
+  if (row.remark4) {
+    let remark4 = JSON.parse(row.remark4);
+    contentArray.value.push({ ...remark4, title: "整改及纠正措施" });
+  }
+  if (row.remark5) {
+    let remark5 = JSON.parse(row.remark5);
+    contentArray.value.push({ ...remark5, title: "整改及纠正措施验证" });
+  }
+  if (row.remark6) {
+    let remark6 = JSON.parse(row.remark6);
+    contentArray.value.push({ ...remark6, title: "处置情况反馈意见" });
+  }
+
+  drawerVisible.value = true;
+};
+
+defineExpose({ showDrawer });
+</script>
+
+<style scoped lang="scss">
+@media print {
+  #print {
+    position: absolute; /* 或 absolute, fixed, 根据需要调整 */
+    top: 20px; /* 调整顶部位置 */
+    margin: 0; /* 重置边距 */
+    width: 1000px;
+  }
+}
+
+.sale-header {
+  text-align: center;
+  font-size: 20px;
+  font-weight: bold;
+  margin-bottom: 10px;
+}
+table {
+  width: 800px;
+  margin: 0 auto;
+  border: 1px solid #000000;
+  border-collapse: collapse;
+}
+
+th,
+td {
+  border: 1px solid #000000;
+  text-align: center;
+  padding: 3px;
+}
+
+.sale-box {
+  .sale-title {
+    font-size: 16px;
+    margin-bottom: 5px;
+    text-align: start;
+  }
+  .sale-content {
+    font-size: 16px;
+    min-height: 60px;
+    margin-bottom: 10px;
+    text-align: start;
+    padding: 0 8px;
+  }
+  .sale-bottom {
+    display: flex;
+    justify-content: flex-end;
+    margin-top: 10px;
+  }
+  .desc {
+    margin-right: 20px;
+  }
+}
+</style>

+ 41 - 0
src/views/sales/handle.vue

@@ -0,0 +1,41 @@
+<template>
+  <div>
+    <el-drawer v-model="drawerVisible" size="80%" :close-on-click-modal="false">
+      <table>
+        <thead>
+          <tr>
+            <th>列标题1</th>
+            <th>列标题2</th>
+            <th>列标题3</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr>
+            <td>行1,列1</td>
+            <td>行1,列2</td>
+            <td>行1,列3</td>
+          </tr>
+          <tr>
+            <td>行2,列1</td>
+            <td>行2,列2</td>
+            <td>行2,列3</td>
+          </tr>
+        </tbody>
+      </table></el-drawer
+    >
+  </div>
+</template>
+
+<script setup>
+const saleModel = ref({});
+const drawerVisible = ref(false);
+
+const showDrawer = (row) => {
+  saleModel.value = row;
+  drawerVisible.value = true;
+};
+
+defineExpose({ showDrawer });
+</script>
+
+<style scoped lang="scss"></style>

+ 208 - 0
src/views/sales/index.vue

@@ -0,0 +1,208 @@
+<template>
+  <div class="container">
+    <div class="header">
+      <Search :searchOptions="searchForm" ref="searchRef" />
+    </div>
+    <div class="table">
+      <el-button
+        type="primary"
+        size="small"
+        class="btn"
+        style="margin-bottom: 10px"
+        @click="toAdd"
+        >新增</el-button
+      >
+      <el-table :data="tableData" border>
+        <el-table-column prop="customer" label="顾客名称" />
+        <el-table-column prop="materialName" label="物料名称" />
+        <el-table-column prop="materialModel" label="物料型号" />
+        <el-table-column prop="contacts" label="联系人" />
+        <el-table-column prop="state" label="审核状态">
+          <template #default="{ row }">
+            <div :style="{ color: getColorByState(row.state) }">
+              {{ getTextByState(row.state) }}
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="created" label="创建时间" />
+        <el-table-column
+          align="center"
+          width="160"
+          prop=""
+          label="操作"
+          id="opear"
+        >
+          <template #default="{ row }">
+            <el-button
+              v-if="row.state === 0"
+              link
+              type="danger"
+              @click="toDelete(row.id)"
+              >删除</el-button
+            >
+            <el-button link @click="handle(row)" type="primary">处理</el-button>
+            <el-button link @click="toDetail(row)" type="primary"
+              >详情</el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+      <Pagination
+        :total="currentOption.total"
+        :page="currentOption.page"
+        :limit="currentOption.limit"
+        :pageSizes="currentOption.pageSizes"
+        v-model:page="currentOption.page"
+        v-model:limit="currentOption.limit"
+        @pagination="getData"
+      />
+      <Add ref="addRef"></Add>
+      <Detail ref="detailRef"></Detail>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import Search from "@/components/Search/index.vue";
+import { useSpcStore } from "@/store";
+import { getSalesData } from "@/api/sales/index";
+import Add from "@/views/sales/add.vue";
+import Detail from "@/views/sales/detail.vue";
+
+defineOptions({
+  name: "SPCSales",
+});
+const store = useSpcStore();
+
+const router = useRouter();
+
+const addRef = ref(null);
+
+const currentOption = reactive({
+  total: 0,
+  page: 1,
+  limit: 10,
+});
+const tableData = ref([]);
+const searchRef = ref(null);
+const searchForm = [
+  {
+    label: "工单号",
+    prop: "workOrderCode",
+    type: "input",
+  },
+  {
+    label: "顾客名称",
+    prop: "custome",
+    type: "input",
+  },
+  {
+    label: "关联订单",
+    prop: "orderNo",
+    type: "input",
+  },
+];
+
+const getData = async (obj) => {
+  const { data, code } = await getSalesData({
+    ...searchRef.value.searchForm,
+    pageNo: currentOption.page,
+    pageSize: currentOption.limit,
+  });
+  if (code == "200") {
+    tableData.value = data.records;
+    currentOption.total = data.totalCount;
+  }
+};
+
+const opInfoData = ref([]);
+const toAdd = () => {
+  addRef.value && addRef.value.showDrawer();
+};
+
+const toDelete = async (id) => {
+  // const { code } = await deleteData({ id });
+  // if (code == "200") {
+  //   ElMessage.success("操作成功");
+  //   getData();
+  // }
+};
+
+// 处理相关
+const handleRef = ref(null);
+const handle = (row) => {};
+
+// 详情
+const detailRef = ref(null);
+const toDetail = (row) => {
+  detailRef.value && detailRef.value.showDrawer(row); // 详情
+};
+
+// 审核状态0待处理 1 审核中 2 审核驳回 3 审核完成
+const getTextByState = (state) => {
+  switch (state) {
+    case 0:
+      return "待处理";
+    case 1:
+      return "审核中";
+    case 2:
+      return "审核驳回";
+    case 3:
+      return "审核完成";
+    default:
+      return "";
+  }
+};
+const getColorByState = (state) => {
+  switch (state) {
+    case 0:
+      return "#59acbb";
+    case 1:
+      return "#bba559";
+    case 2:
+      return "909399";
+    case 3:
+      return "#67c23a";
+    default:
+      return "";
+  }
+};
+
+onMounted(() => {
+  getData();
+});
+</script>
+
+<style scoped lang="scss">
+.container {
+  background-color: white;
+  width: 100%;
+  height: 100%;
+  padding: 20px;
+  display: flex;
+  flex-direction: column;
+  .header {
+    width: 100%;
+    height: auto;
+  }
+  .table {
+    flex: 1;
+    padding-bottom: 20px;
+  }
+  .formView {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    .formTitle {
+      text-align: center;
+      font-size: 20px;
+      margin-bottom: 20px;
+    }
+    .formStyle {
+      width: 400px;
+    }
+  }
+}
+</style>