Просмотр исходного кода

feat: 新建评论管理路由,复制并基于意见反馈的代码进行开发

kuanghua liu 1 год назад
Родитель
Сommit
01689c655d

+ 10 - 0
src/router/full-routes.ts

@@ -468,6 +468,16 @@ const fullRoutes: AppRouteRecordRaw[] = [
           title: '权限管理',
         },
       },
+      {
+        // 权限管理
+        path: 'comments',
+        name: 'SystemComments',
+        component: '/system/comments/CommentsManage',
+        meta: {
+          icon: '',
+          title: '评论管理',
+        },
+      },
       {
         // 意见反馈
         path: 'feedback',

+ 106 - 0
src/views/system/comments/CommentsManage.vue

@@ -0,0 +1,106 @@
+<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" :value="item.value" :key="item.value">{{
+          item.name
+        }}</el-radio-button>
+      </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, index) in feedbackList"
+        :key="index"
+        :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.ts';
+  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>

+ 146 - 0
src/views/system/comments/component/singleProblem.vue

@@ -0,0 +1,146 @@
+<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.submitterUsername }}-{{ props.problemData.submitterNickname }}
+      </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-type">
+      <div>问题类型:</div>
+      <div class="type-content">{{ problemTypeName[props.problemData.problemType] }}</div>
+    </div>
+    <div class="problem-describe">
+      <div>问题描述:</div>
+      <div class="problem-content">{{ props.problemData.problemDescription }}</div>
+    </div>
+    <div class="problem-picture">
+      <div class="picture-content" v-for="(item, index) in problemImageUrls" :key="index">
+        <el-image
+          style="width: 80px; height: 80px"
+          :src="item"
+          :zoom-rate="1.2"
+          :max-scale="7"
+          :min-scale="0.2"
+          :preview-src-list="problemImageUrls"
+          :initial-index="index"
+          fit="cover"
+        />
+      </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.processorName }}</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, computed } from 'vue';
+import { useRouter } from 'vue-router';
+import { Records, STATUS } from '@/api/feedback/feedback';
+import useHandleStore from '../useHandleStore';
+import { storeToRefs } from 'pinia';
+import { problemTypeName } from '../constant';
+
+const useSingle = useHandleStore();
+const { singleFeedback } = storeToRefs(useSingle);
+
+const router = useRouter();
+const props = defineProps<{
+  problemData: Records;
+  isHandle: boolean;
+}>();
+
+const activeNames = ref<string>('');
+const problemImageUrls = computed(() => {
+  const imageUrlString = props.problemData.problemImageUrl;
+  return imageUrlString ? imageUrlString.split(',') : [];
+});
+
+const handleProblem = () => {
+  singleFeedback.value = props.problemData;
+  router.push('/system/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-type,
+.problem-describe {
+  font-size: 14px;
+  white-space: nowrap;
+  word-break: break-word;
+  display: flex;
+  margin-bottom: 8px;
+}
+
+.type-content,
+.problem-content {
+  white-space: pre-wrap;
+  word-break: break-word;
+}
+.type-content {
+  font-weight: 600;
+}
+.problem-picture{
+  display: flex;
+  gap: 20px;
+}
+
+: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>

+ 18 - 0
src/views/system/comments/constant.ts

@@ -0,0 +1,18 @@
+import { STATUS } from '@/api/feedback/feedback';
+
+export const tabContent = [
+  { name: '未处理yyds', value: STATUS.unhandled },
+  { name: '已处理亚克西', value: STATUS.handled },
+];
+
+enum ProblemType {
+  'system' = 1,
+  'product' = 2,
+  'other' = 3,
+}
+
+export const problemTypeName = {
+  [ProblemType.system]: '系统操作问题',
+  [ProblemType.product]: '产品内容问题',
+  [ProblemType.other]: '其他',
+};

+ 130 - 0
src/views/system/comments/handleFeedback.vue

@@ -0,0 +1,130 @@
+<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" :disabled="!isFormValid"> 提交 </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,computed } 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 isFormValid = computed(()=>{
+    return Object.keys(rules).every(item => ruleForm[item].length>0)
+  })
+
+  const backList = () => {
+    router.push('/system/feedback');
+  };
+
+  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,
+        processorName: 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/system/comments/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/system/comments/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;