Parcourir la source

feedback页面

zhudie il y a 1 an
Parent
commit
8069f47d56

+ 3 - 1
.env.development

@@ -14,8 +14,10 @@ VITE_DROP_CONSOLE = true
 # VITE_PROXY=[["/skyeye-admin-api","http://172.16.23.144:8800/api"]]
 #VITE_PROXY=[["/skyeye-admin-api","http://58.144.197.158:19980/skyeye-admin-api"],["/eye_api","http://58.144.197.158:19980/eye_api"],["/push_stream_host","http://58.144.197.158:19980/push_stream_host"],["/skyeye-login","http://58.144.197.158:19980/skyeye-login"]]
 # VITE_PROXY=[["/skyeye-admin-api","http://36.133.176.76:19980/skyeye-admin-api"],["/eye_api","http://36.133.176.76:19980/eye_api"],["/push_stream_host","http://36.133.176.76:19980/push_stream_host"],["/skyeye-login","http://36.133.176.76:19980/skyeye-login"]]
-VITE_PROXY=[["/skyeye-admin-api","http://172.16.23.144/skyeye-admin-api"],[],["/eye_api_bak","http://172.16.23.144/eye_api"],["/push_stream_host","http://172.16.23.144/push_stream_host"],["/skyeye-login","http://172.16.23.144/skyeye-login"],["/ws_api_bak","ws://172.16.23.144/ws_api_bak"]]
+#VITE_PROXY=[["/skyeye-admin-api","http://172.16.23.144/skyeye-admin-api"],[],["/eye_api_bak","http://172.16.23.144/eye_api"],["/push_stream_host","http://172.16.23.144/push_stream_host"],["/skyeye-login","http://172.16.23.144/skyeye-login"],["/ws_api_bak","ws://172.16.23.144/ws_api_bak"]]
 # VITE_PROXY=[["/skyeye-admin-api","http://192.168.32.47/skyeye-admin-api"],["/eye_api","http://192.168.32.47/eye_api"],["/push_stream_host","http:/192.168.32.47/push_stream_host"],["/skyeye-login","http://192.168.32.47/skyeye-login"]]
+VITE_PROXY=[["/skyeye-admin-api","http://192.168.14.68/skyeye-admin-api"],[],["/eye_api_bak","http://192.168.14.68/eye_api"],["/push_stream_host","http://192.168.14.68/push_stream_host"],["/skyeye-login","http://192.168.14.68/skyeye-login"],["/ws_api_bak","ws://192.168.14.68/ws_api_bak"]]
+
 # API 接口地址
 VITE_GLOB_API_URL = 
 # 图片上传地址

+ 63 - 0
src/api/feedback/feedback.ts

@@ -0,0 +1,63 @@
+import { http } from '@/utils/http/axios';
+
+export enum STATUS {
+  handled = 1,
+  unhandled = 2,
+}
+
+export interface Records {
+  id: number;
+  nickname: string;
+  username: string;
+  tenantId: number;
+  mobile: string;
+  problemDescription: string;
+  problemImage: Array<File>;
+  problemStatus: STATUS;
+  processor: string | null;
+  processorTenant: string | null;
+  dealMethod: string | null;
+  createdAt: string;
+  updatedAt: string;
+  isDeleted: number;
+}
+
+export interface ReturnType {
+  records: Records[];
+  pageNumber: number;
+  pageSize: number;
+  totalPage: number;
+  totalRow: number;
+}
+
+export interface QueryFeedback {
+  pageNumber: number;
+  pageSize: number;
+  problemDescription?: string;
+  problemStatus: STATUS;
+}
+
+//查询问题反馈列表
+export const getFeedbackList = (data: QueryFeedback) => {
+  return http.request<ReturnType>({
+    url: `/problemFeedback/getProblemPage`,
+    method: 'post',
+    data,
+  });
+};
+
+export interface UpdateFeedbackType {
+  id: number;
+  problemStatus: STATUS;
+  processor: string;
+  dealMethod: string;
+}
+
+//更新问题反馈
+export const updateFeedback = (data: UpdateFeedbackType) => {
+  return http.request({
+    url: `/problemFeedback/updateProblem`,
+    method: 'post',
+    data,
+  });
+};

BIN
src/assets/icons/handleFeedback.png


BIN
src/assets/icons/phone.png


+ 3 - 1
src/router/router-icons.ts

@@ -1,4 +1,5 @@
-import { renderIcon } from '@/utils/index';
+import { renderIcon, renderImg } from '@/utils/index';
+import HandleFeedback from '@/assets/icons/handleFeedback.png';
 import {
   DashboardOutlined,
   ExclamationCircleOutlined,
@@ -48,4 +49,5 @@ export const constantRouterIcon = {
   CameraOutlined: renderIcon(CameraOutlined),
   ApartmentOutlined: renderIcon(ApartmentOutlined),
   FileTrayFullOutline: renderIcon(FileTrayFullOutline),
+  HandleFeedback: renderImg(HandleFeedback),
 };

+ 114 - 0
src/views/feedback/component/singleProblem.vue

@@ -0,0 +1,114 @@
+<template>
+  <div
+    class="single-item"
+    :style="`padding-bottom:${props.problemData.problemStatus === STATUS.handled ? '0px' : '38px'}`"
+  >
+    <div style="display: flex; font-size: 12px">
+      <img src="@/assets/icons/phone.png" alt="" />
+      <div class="single-contact">联系方式:{{ props.problemData.mobile }}</div>
+      <div style="color: #00000073"
+        >人员:{{ props.problemData.username }}-{{ props.problemData.nickname }}
+      </div>
+      <div style="margin-left: 20px; color: #00000073">日期:{{ props.problemData.createdAt }}</div>
+    </div>
+    <el-button v-if="props.isHandle" type="primary" class="single-handle" @click="handleProblem"
+      >处理</el-button
+    >
+    <el-divider />
+    <div class="problem-describe">
+      <div>问题描述:</div>
+      <div class="problem-content">{{ props.problemData.problemDescription }}</div>
+    </div>
+
+    <el-collapse v-if="props.problemData.problemStatus === STATUS.handled" v-model="activeNames">
+      <el-collapse-item name="1">
+        <template #title>
+          <div class="problem-describe">
+            <div>处理人:</div>
+            <div class="problem-content">{{ props.problemData.processor }}</div>
+          </div>
+        </template>
+        <div class="problem-describe">
+          <div>处理措施:</div>
+          <div class="problem-content">{{ props.problemData.dealMethod }}</div>
+        </div>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import { useRouter } from 'vue-router';
+  import { Records, STATUS } from '@/api/feedback/feedback';
+  import useHandleStore from '../useHandleStore';
+  import { storeToRefs } from 'pinia';
+
+  const useSingle = useHandleStore();
+  const { singleFeedback } = storeToRefs(useSingle);
+
+  const router = useRouter();
+  const props = defineProps<{
+    problemData: Records;
+    isHandle: boolean;
+  }>();
+
+  const activeNames = ref<string>('');
+
+  const handleProblem = () => {
+    singleFeedback.value = props.problemData;
+    router.push('/feedback/handle');
+  };
+</script>
+
+<style scoped>
+  .single-item {
+    background: #ffffff;
+    box-shadow: 0px 1px 8px 0px rgba(0, 0, 0, 0.09);
+    border-radius: 6px;
+    padding: 16px 12px 0px 12px;
+    position: relative;
+  }
+
+  .single-contact {
+    margin-left: 10px;
+    margin-right: 40px;
+  }
+  .single-handle {
+    position: absolute;
+    top: 11px;
+    right: 12px;
+  }
+
+  .el-divider--horizontal {
+    margin: 16px 0;
+  }
+
+  .problem-describe {
+    font-size: 14px;
+    white-space: nowrap;
+    word-break: break-word;
+    display: flex;
+    margin-bottom: 8px;
+  }
+
+  .problem-content {
+    white-space: pre-wrap;
+    word-break: break-word;
+  }
+
+  :deep(.el-collapse-item__header) {
+    border-bottom: none;
+  }
+
+  :deep(.el-collapse-item__wrap) {
+    border-bottom: none;
+  }
+
+  :deep(.el-collapse-item__content) {
+    padding-bottom: 0px;
+  }
+  :deep(.el-collapse) {
+    border-top: none;
+  }
+</style>

+ 6 - 0
src/views/feedback/constant.ts

@@ -0,0 +1,6 @@
+import { STATUS } from '@/api/feedback/feedback';
+
+export const tabContent = [
+  { name: '未处理', value: STATUS.unhandled },
+  { name: '已处理', value: STATUS.handled },
+];

+ 111 - 0
src/views/feedback/feedback.vue

@@ -0,0 +1,111 @@
+<template>
+  <div class="feedback-page">
+    <div class="feedback-head">
+      <el-radio-group
+        v-model="problemStatus"
+        class="radio-button"
+        text-color="#1677FF"
+        fill="#1890FF26"
+        @change="handleClick"
+      >
+        <el-radio-button v-for="item in tabContent" :label="item.name" :value="item.value" />
+      </el-radio-group>
+      <div class="search-input">
+        <el-input
+          v-model="queryDescription"
+          style="width: 294px"
+          placeholder="请输入搜索内容"
+          :prefix-icon="Search"
+        />
+        <el-button type="primary" style="margin-left: 16px" @click="searchFeedback"
+          >搜索</el-button
+        ></div
+      ></div
+    >
+    <div class="problem-list"
+      ><singleProblem
+        v-for="item in feedbackList"
+        :problem-data="item"
+        :is-handle="item.problemStatus === STATUS.unhandled"
+        style="margin-top: 22px; margin-bottom: 2px"
+    /></div>
+
+    <el-pagination
+      v-model:current-page="pageNumber"
+      v-model:page-size="pageSize"
+      background
+      layout="prev, pager, next,sizes,jumper"
+      :page-sizes="[5, 10, 20, 40]"
+      :total="totalRow"
+      @size-change="handleSizeChange"
+      @current-change="handleCurrentChange"
+      class="flex justify-end"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { Search } from '@element-plus/icons-vue';
+  import { tabContent } from './constant';
+  import singleProblem from './component/singleProblem.vue';
+  import useFeedback from './use-feedback.ts';
+  import { STATUS } from '@/api/feedback/feedback';
+
+  const useFeedbackList = useFeedback();
+  const { feedbackList, pageNumber, pageSize, problemStatus, queryDescription, totalRow, getList } =
+    useFeedbackList;
+
+  const handleClick = () => {
+    queryDescription.value = '';
+    getList();
+  };
+
+  const searchFeedback = () => {
+    pageNumber.value = 1;
+    getList();
+  };
+
+  const handleSizeChange = () => {
+    pageNumber.value = 1;
+    getList();
+  };
+  const handleCurrentChange = () => {
+    getList();
+  };
+</script>
+
+<style scoped>
+  .feedback-page {
+    position: relative;
+    height: calc(100vh - 64px - 12px);
+    background-color: #ffffff;
+    padding-top: 20px;
+    padding-left: 20px;
+    padding-right: 20px;
+  }
+
+  :deep(.el-radio-button .el-radio-button__inner) {
+    width: 188px;
+    background-color: #00000005;
+  }
+
+  .feedback-head {
+    display: flex;
+    flex-direction: column;
+  }
+
+  .search-input {
+    margin-top: 24px;
+  }
+
+  .problem-list {
+    height: calc(100vh - 270px);
+    overflow: auto;
+    margin-bottom: 4px;
+    padding: 0 2px;
+  }
+
+  :deep(.el-pagination .el-select) {
+    width: 92px;
+  }
+</style>

+ 127 - 0
src/views/feedback/handleFeedback.vue

@@ -0,0 +1,127 @@
+<template>
+  <div class="feedback-page">
+    <el-button :icon="Back" @click="backList">返回</el-button>
+    <div
+      ><singleProblem
+        :problem-data="singleFeedback"
+        :is-handle="false"
+        style="margin-top: 24px; margin-bottom: 40px"
+    /></div>
+    <el-form
+      ref="ruleFormRef"
+      :model="ruleForm"
+      :rules="rules"
+      label-position="left"
+      label-width="90px"
+      class="demo-ruleForm"
+      size="default"
+      status-icon
+    >
+      <el-form-item label="处理人:" prop="name">
+        <el-input v-model="ruleForm.name" placeholder="请输入" style="width: 160px" />
+      </el-form-item>
+      <el-form-item label="处理措施:" prop="solution">
+        <el-input
+          v-model="ruleForm.solution"
+          placeholder="请输入多行文字"
+          :autosize="{ minRows: 7, maxRows: 7 }"
+          type="textarea"
+          style="height: 160px"
+        />
+      </el-form-item>
+    </el-form>
+    <el-button type="primary" @click="subHandle" class="sub-btn"> 提交 </el-button>
+
+    <el-dialog
+      v-model="dialogVisible"
+      title="提交成功"
+      width="500"
+      align-center
+      @close="closeDialog"
+    >
+      <span>提交成功,可以在【已处理】中查看</span>
+      <template #footer>
+        <div class="dialog-footer">
+          <!-- <el-button @click="dialogVisible = false">Cancel</el-button> -->
+          <el-button type="primary" @click="closeDialog"> 确定 </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ref, reactive, watch } from 'vue';
+  import { Back } from '@element-plus/icons-vue';
+  import { useRouter } from 'vue-router';
+  import singleProblem from './component/singleProblem.vue';
+  import type { FormInstance, FormRules } from 'element-plus';
+  import useHandleStore from './useHandleStore';
+  import { storeToRefs } from 'pinia';
+  import { STATUS, updateFeedback } from '@/api/feedback/feedback';
+
+  const useSingleStore = useHandleStore();
+  const { singleFeedback } = storeToRefs(useSingleStore);
+
+  const router = useRouter();
+
+  const ruleFormRef = ref<FormInstance>();
+  interface RuleForm {
+    name: string;
+    solution: string;
+  }
+  const ruleForm = reactive<RuleForm>({
+    name: '',
+    solution: '',
+  });
+
+  const rules = reactive<FormRules>({
+    name: [{ required: true, message: '处理人不能为空', trigger: 'blur' }],
+  });
+
+  const backList = () => {
+    router.push('/feedback/list');
+  };
+
+  const dialogVisible = ref<boolean>(false);
+
+  const subHandle = () => {
+    if (!ruleFormRef.value) return;
+    ruleFormRef.value.validate((valid) => {
+      if (!valid) {
+        return;
+      }
+
+      const subData = {
+        id: singleFeedback.value.id,
+        problemStatus: STATUS.handled,
+        processor: ruleForm.name,
+        dealMethod: ruleForm.solution,
+      };
+      updateFeedback(subData).then(() => {
+        dialogVisible.value = true;
+      });
+    });
+  };
+
+  const closeDialog = () => {
+    dialogVisible.value = false;
+    backList();
+  };
+</script>
+
+<style scoped>
+  .feedback-page {
+    position: relative;
+    height: calc(100vh - 64px - 12px);
+    background-color: #ffffff;
+    padding-top: 20px;
+    padding-left: 20px;
+    padding-right: 20px;
+  }
+
+  .sub-btn {
+    margin-top: 6px;
+    margin-left: 88px;
+  }
+</style>

+ 46 - 0
src/views/feedback/use-feedback.ts

@@ -0,0 +1,46 @@
+import { STATUS, Records, QueryFeedback, getFeedbackList } from '@/api/feedback/feedback';
+import { onMounted, ref } from 'vue';
+
+export function useFeedbackList() {
+  const feedbackList = ref<Records[]>([]);
+  const pageNumber = ref<number>(1);
+  const pageSize = ref<number>(10);
+  const problemStatus = ref<STATUS>(STATUS.unhandled);
+  const queryDescription = ref<string>('');
+  const totalPage = ref<number>();
+  const totalRow = ref<number>();
+
+  const getList = () => {
+    const params: QueryFeedback = {
+      pageNumber: pageNumber.value,
+      pageSize: pageSize.value,
+      //problemDescription: queryDescription.value
+      problemStatus: problemStatus.value,
+    };
+    if (queryDescription.value) {
+      params.problemDescription = queryDescription.value;
+    }
+    getFeedbackList(params).then((res) => {
+      feedbackList.value = res.records;
+      totalPage.value = res.totalPage;
+      totalRow.value = res.totalRow;
+    });
+  };
+
+  onMounted(() => {
+    getList();
+  });
+
+  return {
+    feedbackList,
+    pageNumber,
+    pageSize,
+    problemStatus,
+    queryDescription,
+    totalPage,
+    totalRow,
+    getList,
+  };
+}
+
+export default useFeedbackList;

+ 11 - 0
src/views/feedback/useHandleStore.ts

@@ -0,0 +1,11 @@
+import { ref } from 'vue';
+import { defineStore } from 'pinia';
+import { Records } from '@/api/feedback/feedback';
+
+export const useFeedbackHandled = defineStore('handle-feedback', () => {
+  const singleFeedback = ref<Records>({} as Records);
+
+  return { singleFeedback };
+});
+
+export default useFeedbackHandled;