tangning hace 1 año
padre
commit
81726d23cc

+ 11 - 11
src/api/customer/index.ts

@@ -77,14 +77,14 @@ export const downTemplate = (params: companyExcelParams) =>
   });
 
 export function companyUploadExcel(
-    params: UploadFileParams,
-    onUploadProgress: (progressEvent: ProgressEvent) => void,
-  ) {
-    return defHttp.uploadFile<Result>(
-      {
-        url: Api.companyUploadExcel,
-        onUploadProgress,
-      },
-      params,
-    );
-   }
+  params: UploadFileParams,
+  onUploadProgress: (progressEvent: ProgressEvent) => void,
+) {
+  return defHttp.uploadFile<Result>(
+    {
+      url: Api.companyUploadExcel,
+      onUploadProgress,
+    },
+    params,
+  );
+}

+ 114 - 1
src/api/operate/index.ts

@@ -12,7 +12,7 @@ import {
   updateParams,
   overallDelete,
 } from './model';
-import { Result } from '/#/axios';
+import { Result, FileStream, UploadFileParams } from '/#/axios';
 
 enum Api {
   pageList = '/service/manage/news/pageNews',
@@ -62,6 +62,16 @@ enum Api {
   roomList = '/service/manage/takeLook/roomList',
   updateRoomShow = '/service/manage/takeLook/updateRoomShow',
   deleteRoom = '/service/manage/takeLook/deleteRoom',
+  // 用户反馈管理
+  scoreAug = '/service/manage/feedback/scoreAug',
+  feedbackList = '/service/manage/feedback/list',
+  feedbackOptionList = '/service/manage/feedbackOption/list',
+  getAllByTypeId = '/service/manage/feedback/h5/getAllByTypeId/',
+  feedbackOptiondelete = '/service/manage/feedbackOption/delete',
+  feedbackOptionAdd = '/service/manage/feedbackOption/add',
+  feedbackOptionDel = '/service/manage/feedbackOption/update',
+  feedbackhandle = '/service/manage/feedback/handle',
+  downTemplate = '/service/manage/feedback/export',
 }
 
 /**
@@ -518,3 +528,106 @@ export const deleteRoom = (params) =>
       ignoreCancelToken: true,
     },
   });
+export const getfeedbackList = (params) =>
+  defHttp.post<Result>({
+    url: Api.feedbackList,
+    params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+export const getScoreAug = (params: PageParams) =>
+  defHttp.get<Result>({
+    url: Api.scoreAug,
+    params: params,
+    // data: params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+export const getAllByTypeId = (typeId) =>
+  defHttp.get<Result>({
+    url: Api.getAllByTypeId + typeId,
+    params: {},
+    // data: params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+export const feedbackOptionList = (params) =>
+  defHttp.post<Result>({
+    url: Api.feedbackOptionList,
+    params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+
+export const feedbackOptiondelete = (params) =>
+  defHttp.post<Result>({
+    url: Api.feedbackOptiondelete,
+    params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+export const feedbackOptionDel = (params) =>
+  defHttp.post<Result>({
+    url: Api.feedbackOptionDel,
+    params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+
+export const feedbackOptionAdd = (params) =>
+  defHttp.post<Result>({
+    url: Api.feedbackOptionAdd,
+    params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+
+export const feedbackhandle = (params) =>
+  defHttp.post<Result>({
+    url: Api.feedbackhandle,
+    params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+
+export const downTemplate = (params) =>
+  defHttp.downloadFile<FileStream>({
+    method: 'GET',
+    url: Api.downTemplate,
+    params: params,
+    // data: params,
+    fileName: '用户反馈管理.xlsx',
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+    responseType: 'blob',
+  });
+export function companyUploadExcel(
+  params: UploadFileParams,
+  onUploadProgress: (progressEvent: ProgressEvent) => void,
+) {
+  return defHttp.uploadFile<Result>(
+    {
+      url: Api.companyUploadExcel,
+      onUploadProgress,
+    },
+    params,
+  );
+}

+ 123 - 120
src/api/product/index.ts

@@ -1,6 +1,13 @@
 import { defHttp } from '/@/utils/http/axios';
-import { PageParams, RentListGetResultModel,InvoiceListResul, addCameraParams, updateParams,getItemParams } from './model';
-import { Result,UploadFileParams } from '/#/axios';
+import {
+  PageParams,
+  RentListGetResultModel,
+  InvoiceListResul,
+  addCameraParams,
+  updateParams,
+  getItemParams,
+} from './model';
+import { Result, UploadFileParams } from '/#/axios';
 import { ContentTypeEnum } from '/@/enums/httpEnum';
 
 enum Api {
@@ -23,7 +30,7 @@ enum Api {
   appFileDelete = '/service/manage/appFile/delete',
   tipList = '/service/manage/serviceUpTip/list',
   tipUpdata = '/service/manage/serviceUpTip/saveOrUpdate',
-  tipDelete = '/service/manage/serviceUpTip/delete'
+  tipDelete = '/service/manage/serviceUpTip/delete',
 }
 
 /**
@@ -44,43 +51,42 @@ export const CameraList = (params: PageParams) =>
 /**
  * @description: 新增固件
  */
-export const AddAndUpload = (params: PageParams) =>{
+export const AddAndUpload = (params: PageParams) => {
   return defHttp.uploadFile<Result>(
     {
       url: Api.addAndUpload,
     },
-    params
+    params,
   );
-}
+};
 /**
  * @description: 编辑固件
  */
 
 export const EditAndUpload = (params: PageParams) =>
-defHttp.post<Result>({
-  url: Api.editAndUpload,
-  params,
-  // data: params,
-  headers: {
-    // @ts-ignore
-    ignoreCancelToken: true,
-  },
-});
-
+  defHttp.post<Result>({
+    url: Api.editAndUpload,
+    params,
+    // data: params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
 
 /**
  * @description: 删除固件
  */
- export const DelAndUpload = (params: PageParams) =>
- defHttp.post<Result>({
-   url: Api.delAndUpload,
-   params,
-   // data: params,
-   headers: {
-     // @ts-ignore
-     ignoreCancelToken: true,
-   },
- });
+export const DelAndUpload = (params: PageParams) =>
+  defHttp.post<Result>({
+    url: Api.delAndUpload,
+    params,
+    // data: params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
 
 export const SpaceSdkOnline = (params: PageParams) =>
   defHttp.post<Result>({
@@ -107,76 +113,76 @@ export const SpaceSdkUpdate = (params: PageParams) =>
 /**
  * @description: SDK list
  */
- export const SpaceSdkList = (params: PageParams) =>
- defHttp.post<Result>({
-   url: Api.spaceSdkList,
-   params,
-   // data: params,
-   headers: {
-     // @ts-ignore
-     ignoreCancelToken: true,
-   },
- });
+export const SpaceSdkList = (params: PageParams) =>
+  defHttp.post<Result>({
+    url: Api.spaceSdkList,
+    params,
+    // data: params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
 
 export const SpaceSdkTop = (params: addCameraParams) =>
- defHttp.post<RentListGetResultModel>({
-   url: Api.spaceSdkTop,
-   params,
-   headers: {
-     // @ts-ignore
-     ignoreCancelToken: true,
-   },
- });
+  defHttp.post<RentListGetResultModel>({
+    url: Api.spaceSdkTop,
+    params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
 
 export const SpaceSdkDelete = (params: updateParams) =>
- defHttp.post<Result>({
-   url: Api.spaceSdkDelete,
-   params,
-   headers: {
-     // @ts-ignore
-     ignoreCancelToken: true,
-   },
- });
+  defHttp.post<Result>({
+    url: Api.spaceSdkDelete,
+    params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
 //app 管理
 export const AppFileAdd = (params: updateParams) =>
- defHttp.post<Result>({
-   url: Api.appFileAdd,
-   params,
-   headers: {
-     // @ts-ignore
-     ignoreCancelToken: true,
-   },
- });
-
- export const AppFileAgentList = (params: updateParams) =>
- defHttp.get<Result>({
-   url: Api.appFileAgentList,
-   params,
-   headers: {
-     // @ts-ignore
-     ignoreCancelToken: true,
-   },
- });
+  defHttp.post<Result>({
+    url: Api.appFileAdd,
+    params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+
+export const AppFileAgentList = (params: updateParams) =>
+  defHttp.get<Result>({
+    url: Api.appFileAgentList,
+    params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
 
 export const AppFileList = (params: updateParams) =>
- defHttp.post<Result>({
-   url: Api.appFileList,
-   params,
-   headers: {
-     // @ts-ignore
-     ignoreCancelToken: true,
-   },
- });
- 
+  defHttp.post<Result>({
+    url: Api.appFileList,
+    params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+
 export const AppFileDelete = (params: updateParams) =>
-defHttp.post<Result>({
-  url: Api.appFileDelete,
-  params,
-  headers: {
-    // @ts-ignore
-    ignoreCancelToken: true,
-  },
-});
+  defHttp.post<Result>({
+    url: Api.appFileDelete,
+    params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
 //上传文件
 export function SpaceSdkUpload(
   params: UploadFileParams,
@@ -189,7 +195,7 @@ export function SpaceSdkUpload(
     },
     params,
   );
- }
+}
 export function uploadApi(
   params: UploadFileParams,
   onUploadProgress: (progressEvent: ProgressEvent) => void,
@@ -201,8 +207,8 @@ export function uploadApi(
     },
     params,
   );
- }
- 
+}
+
 export function AppFileUpload(
   params: UploadFileParams,
   onUploadProgress: (progressEvent: ProgressEvent) => void,
@@ -214,37 +220,34 @@ export function AppFileUpload(
     },
     params,
   );
- }
+}
 
-  
 export const tipList = (params) =>
-defHttp.post<Result>({
-  url: Api.tipList,
-  params,
-  headers: {
-    // @ts-ignore
-    ignoreCancelToken: true,
-  },
-});
-
- 
+  defHttp.post<Result>({
+    url: Api.tipList,
+    params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+
 export const tipDelete = (params) =>
-defHttp.post<Result>({
-  url: Api.tipDelete,
-  params,
-  headers: {
-    // @ts-ignore
-    ignoreCancelToken: true,
-  },
-});
-
- 
+  defHttp.post<Result>({
+    url: Api.tipDelete,
+    params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });
+
 export const tipUpdata = (params) =>
-defHttp.post<Result>({
-  url: Api.tipUpdata,
-  params,
-  headers: {
-    // @ts-ignore
-    ignoreCancelToken: true,
-  },
-});
+  defHttp.post<Result>({
+    url: Api.tipUpdata,
+    params,
+    headers: {
+      // @ts-ignore
+      ignoreCancelToken: true,
+    },
+  });

+ 35 - 0
src/views/operate/components/GrowCard.vue

@@ -0,0 +1,35 @@
+<template>
+  <div class="md:flex">
+    <template v-for="(item, index) in list" :key="item.title">
+      <Card
+        size="small"
+        :loading="loading"
+        :title="item.nameCn"
+        class="md:w-1/4 w-full !md:mt-0 !mt-4"
+        :class="[index != list.length && '!md:mr-4']"
+        :canExpan="false"
+      >
+        <div class="py-4 px-4 flex justify-between">
+          <div class="" v-if="item.score == '暂无评分' || item.score == 0">暂无评分</div>
+          <CountTo v-else prefix="分" :startVal="1" :endVal="item.score" class="text-2xl" />
+        </div>
+      </Card>
+    </template>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { CountTo } from '/@/components/CountTo/index';
+  import { Icon } from '/@/components/Icon';
+  import { Tag, Card } from 'ant-design-vue';
+  import { growCardList, GrowCardItem } from '../data';
+
+  defineProps({
+    loading: {
+      type: Boolean,
+    },
+    list: {
+      type: Array as PropType<Array<GrowCardItem>>,
+      default: [],
+    },
+  });
+</script>

+ 139 - 0
src/views/operate/components/config/addModal.vue

@@ -0,0 +1,139 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    @register="register"
+    :title="fileFlow.title"
+    @visible-change="handleVisibleChange"
+    @cancel="resetFields"
+    @ok="handleSubmit"
+  >
+    <div class="pt-2px pr-3px">
+      <BasicForm @register="registerForm" :model="model" >
+        <template #text="{ model, field }">
+          {{ model[field]  }}
+        </template>
+      </BasicForm>
+    </div>
+  </BasicModal>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, nextTick, onMounted, reactive, h } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { uploadApi } from '/@/api/product/index';
+  import { feedbackOptionDel, feedbackOptionAdd } from '/@/api/operate'
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import { Tinymce } from '/@/components/Tinymce/index';
+  import { intercomMessageHandle } from '/@/api/operate'
+
+  const { t } = useI18n();
+  export default defineComponent({
+    components: { BasicModal, BasicForm },
+    props: {
+      userData: { type: Object },
+      typeId: { type: Number },
+    },
+    emits: ['update', 'register'],
+    setup(props, { emit }) {
+      const modelRef = ref({});
+      const fileFlow = reactive({
+        title: '修改配置',
+      })
+      const { createMessage } = useMessage();
+      const schemas: FormSchema[] = [
+        {
+          field: 'id',
+          component: 'Input',
+          show:false,
+          label: 'id',
+          required: false,
+        },
+        {
+          field: 'nameCn',
+          component: 'Input',
+          required: true,
+          label: '选项(中文)',
+          componentProps: {
+            maxlength: 50,
+          },
+          colProps: {
+            span: 22,
+          },
+        },
+        {
+          field: 'nameEn',
+          component: 'Input',
+          required: true,
+          label: '选项(英文)',
+          componentProps: {
+            maxlength: 50,
+          },
+          colProps: {
+            span: 22,
+          },
+        },
+      ];
+      const [registerForm, { validate, resetFields, setFieldsValue, updateSchema }] = useForm({
+        labelWidth: 120,
+        schemas,
+        showActionButtonGroup: false,
+        actionColOptions: {
+          span: 24,
+        },
+      });
+      onMounted(() => {});
+      let addListFunc = () => {};
+      const [register, { closeModal }] = useModalInner((data) => {
+        fileFlow.title = data && data.id ? '修改配置' : '新增配置';
+        data && onDataReceive(data);
+      });
+      async function onDataReceive(data) {
+        console.log('data',data);
+        modelRef.value = data
+        resetFields();
+        setFieldsValue(data);
+      }
+      function NewTypeChange(val){
+        console.log('NewTypeChange',val)
+        updateSchema([
+          { field: 'content', ifShow:val == 2,},
+          { field: 'newsUrl', ifShow:val != 2,},
+        ])
+      } 
+      const handleSubmit = async () => {
+        const params = await validate();
+        const apiData = {
+          ...params as any,
+          typeId: props.typeId,
+        }
+        let api = params.id ? feedbackOptionDel : feedbackOptionAdd
+        try {
+          await api(apiData);
+          closeModal();
+          resetFields();
+          createMessage.success(t('common.optSuccess'));
+          emit('update');
+        } catch (error) {
+          console.log('not passing', error);
+        }
+      };
+      function handleVisibleChange(v) {
+        v && props.userData && nextTick(() => onDataReceive(props.userData));
+      }
+      return {
+        register,
+        schemas,
+        registerForm,
+        model: modelRef,
+        fileFlow,
+        NewTypeChange,
+        handleVisibleChange,
+        handleSubmit,
+        addListFunc,
+        resetFields,
+        t,
+      };
+    },
+  });
+</script>

+ 63 - 0
src/views/operate/components/config/bofang.vue

@@ -0,0 +1,63 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    @register="register"
+    :defaultFullscreen="true"
+    title="视频"
+    @visible-change="handleVisibleChange"
+    @ok="handleSubmit"
+    :showOkBtn="false"
+  >
+    <video
+      ref="myVideo"
+      :src="src"
+      controls autoplay
+      style="height: 100%; width: 100%"
+    ></video>
+  </BasicModal>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, nextTick, onMounted, reactive, h } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { uploadApi } from '/@/api/product/index';
+  import { feedbackOptionDel, feedbackOptionAdd } from '/@/api/operate';
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import { Tinymce } from '/@/components/Tinymce/index';
+  import { intercomMessageHandle } from '/@/api/operate';
+
+  const { t } = useI18n();
+  export default defineComponent({
+    components: { BasicModal, BasicForm },
+    props: {
+      userData: { type: Object },
+      typeId: { type: Number },
+    },
+    emits: ['update', 'register'],
+    setup(props, { emit }) {
+      const myVideo = ref(null);
+      const src = ref(null);
+      const { createMessage } = useMessage();
+      onMounted(() => {});
+      let addListFunc = () => {};
+      const [register, { closeModal }] = useModalInner((data) => {
+        console.log('useModalInner', data);
+        src.value = data;
+        myVideo.value && myVideo.value.play();
+      });
+      const handleSubmit = async () => {
+        closeModal();
+      };
+      function handleVisibleChange(v) {}
+      return {
+        register,
+        handleVisibleChange,
+        handleSubmit,
+        addListFunc,
+        t,
+        src,
+      };
+    },
+  });
+</script>

+ 262 - 0
src/views/operate/components/config/messgeModal.vue

@@ -0,0 +1,262 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    @register="register"
+    title="处理反馈"
+    @visible-change="handleVisibleChange"
+    okText="完成处理"
+    @cancel="resetFields"
+    @ok="handleSubmit"
+  >
+    <div class="pt-2px pr-3px">
+      <BasicForm @register="registerForm" :model="model">
+        <template #text="{ model, field }">
+          {{ model[field] }}
+        </template>
+        <template #text1="{ model, field }">
+          {{ model[field]?.nameCn }}
+        </template>
+        <template #img="{ model, field }">
+          <div class="imgText">
+            <p style="line-height:30px; margin: 0"> {{ model[field] }}</p>
+            <PreviewGroup>
+              <template v-for="item in model[field + 'Imgs']" :key="item">
+                <div class="video" @click="bofang(item)" style="display: inline-block;position: relative;" v-if="checkMediaType(item) == 'video'">
+                  <Image
+                    style="overflow: hidden; height: 100%; object-fit: cover; margin-right: 10px; cursor: none;"
+                    :height="80"
+                    :width="80"
+                    :key="item"
+                    :src="item + '?spm=qipa250&x-oss-process=video/snapshot,t_7000,f_jpg,w_800,h_600,m_fast'"
+                    :size="200"
+                  />
+                  <YoutubeOutlined style="font-size:32px; color: #fff;position:absolute;left:24px;top:24px" />
+                </div>
+                <Image
+                  v-else
+                  style="overflow: hidden; height: 100%; object-fit: cover; margin-right: 10px"
+                  :height="80"
+                  :width="80"
+                  :key="item"
+                  :src="item"
+                  :size="200"
+                />
+              </template>
+            </PreviewGroup>
+          </div>
+        </template>
+      </BasicForm>
+      <videoModal @register="registerbofang" />
+    </div>
+  </BasicModal>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, nextTick, onMounted, reactive, h } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { uploadApi } from '/@/api/product/index';
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import { Tinymce } from '/@/components/Tinymce/index';
+  import { useModal } from '/@/components/Modal';
+  import { feedbackhandle } from '/@/api/operate';
+  import { Image } from 'ant-design-vue';
+  import { YoutubeOutlined } from '@ant-design/icons-vue';
+  import videoModal from './bofang.vue'
+  const { t } = useI18n();
+  export default defineComponent({
+    components: { BasicModal, videoModal, BasicForm, Image, PreviewGroup: Image.PreviewGroup, YoutubeOutlined },
+    props: {
+      userData: { type: Object },
+    },
+    emits: ['update', 'register'],
+    setup(props, { emit }) {
+      const modelRef = ref({});
+      const fileFlow = reactive({
+        coverImageUrl: '',
+      });
+      const { createMessage } = useMessage();
+      const [registerbofang, { openModal }] = useModal();
+      const schemas: FormSchema[] = [
+        {
+          field: 'id',
+          component: 'Input',
+          show: false,
+          label: 'id',
+        },
+        {
+          field: 'solutionImgs',
+          component: 'Input',
+          show: false,
+          label: 'id',
+        },
+        {
+          field: 'problemDescImgs',
+          component: 'Input',
+          show: false,
+          label: 'id',
+        },
+        {
+          field: 'problemDesc',
+          component: 'Input',
+          slot: 'img',
+          label: '问题描述',
+          defaultValue: 'cn',
+        },
+        {
+          field: 'hardwareOption',
+          component: 'Input',
+          slot: 'text1',
+          label: '硬件产品',
+        },
+        {
+          field: 'softwareOption',
+          slot: 'text1',
+          component: 'Input',
+          label: '软件产品',
+        },
+        {
+          field: 'industryOption',
+          slot: 'text1',
+          component: 'Input',
+          label: '所在行业',
+        },
+        {
+          field: 'solution',
+          component: 'Input',
+          slot: 'img',
+          label: '期望解决方案',
+        },
+        {
+          field: 'nickName',
+          slot: 'text',
+          component: 'Input',
+          label: '姓名',
+        },
+        {
+          field: 'phone',
+          slot: 'text',
+          component: 'Input',
+          label: '联系方式',
+        },
+        {
+          field: 'address',
+          slot: 'text',
+          component: 'Input',
+          label: '地址',
+        },
+        {
+          field: 'score',
+          slot: 'text',
+          component: 'Input',
+          label: '评分',
+        },
+        {
+          field: 'scoreReason',
+          slot: 'text',
+          component: 'Input',
+          label: '评分理由',
+        },
+        {
+          field: 'result',
+          component: 'InputTextArea',
+          // required: true,
+          label: '处理结果',
+          componentProps: {
+            maxLength: 200,
+            rows: 4,
+            placeholder: '请输入处理结果',
+          },
+          colProps: {
+            span: 22,
+          },
+        },
+      ];
+      const [registerForm, { validate, resetFields, setFieldsValue, updateSchema }] = useForm({
+        labelWidth: 120,
+        schemas,
+        showActionButtonGroup: false,
+        actionColOptions: {
+          span: 24,
+        },
+      });
+      onMounted(() => {});
+      let addListFunc = () => {};
+      const [register, { closeModal }] = useModalInner((data) => {
+        data && onDataReceive(data);
+      });
+      async function onDataReceive(data) {
+        modelRef.value = data;
+        updateSchema({ field: 'result', slot: data.status == 1 ? 'text' : '' });
+        resetFields();
+        setFieldsValue(data);
+      }
+      const handleSubmit = async () => {
+        const params = await validate();
+        try {
+          await feedbackhandle(params);
+          closeModal();
+          resetFields();
+          createMessage.success(t('common.optSuccess'));
+          emit('update');
+        } catch (error) {
+          console.log('not passing', error);
+        }
+      };
+
+      function checkMediaType(url) {
+        // 创建URL对象
+        var link = new URL(url);
+
+        // 获取路径部分(去除参数)
+        var path = link.pathname;
+
+        // 获取路径的最后一个点之后的内容作为文件扩展名
+        var extension = path.split('.').pop().toLowerCase();
+
+        // 声明支持的图片和视频文件扩展名
+        var imageExtensions = ['jpg', 'jpeg', 'gif', 'png'];
+        var videoExtensions = ['mp4', 'wmv', 'avi', 'mov'];
+
+        // 判断文件扩展名是否在图片扩展名数组中
+        if (imageExtensions.includes(extension)) {
+          return 'image';
+        }
+
+        // 判断文件扩展名是否在视频扩展名数组中
+        if (videoExtensions.includes(extension)) {
+          return 'video';
+        }
+
+        // 扩展名不在图片或视频数组中,返回null表示无法确定媒体类型
+        return null;
+      }
+      function handleVisibleChange(v) {
+        v && props.userData && nextTick(() => onDataReceive(props.userData));
+      }
+      function bofang(src){
+        openModal(true, src);
+      }
+      return {
+        register,
+        schemas,
+        registerForm,
+        model: modelRef,
+        fileFlow,
+        handleVisibleChange,
+        handleSubmit,
+        addListFunc,
+        resetFields,
+        checkMediaType,
+        registerbofang,
+        bofang,
+        t,
+      };
+    },
+  });
+</script>
+<style lang="less" scoped>
+  ::v-deep .ant-image {
+    margin-right: 10px;
+  }
+</style>

+ 176 - 0
src/views/operate/configuration.vue

@@ -0,0 +1,176 @@
+<template>
+  <PageWrapper contentBackground>
+    <template #footer>
+      <a-tabs v-model:activeKey="state" @change="changeTable">
+        <a-tab-pane :key="1" tab="所在行业" />
+        <a-tab-pane :key="2" tab="硬件产品" />
+        <a-tab-pane :key="3" tab="软件产品" />
+      </a-tabs>
+      <a-button style="position: absolute; right: 20px; top: 30px;" v-if="getCheckPerm('recruit-add')" @click="router.go(-1)"> 返回 </a-button>
+    </template>
+
+    <div class="desc-wrap-BasicTable">
+      <BasicTable @register="registerTimeTable">
+        <template #toolbar>
+          <a-button type="primary" v-if="getCheckPerm('recruit-add')" @click="openModal(true, {})">
+            新增
+          </a-button>
+        </template>
+        <template #action="{ record }">
+          <TableAction
+            stopButtonPropagation
+            :actions="[
+              {
+                label: '编辑',
+                //icon: 'icon-park-outline:door-handle',
+                //ifShow: getCheckPerm('agent-deal') && record.state == 0,
+                onClick: handleEidt.bind(null, record),
+              },
+              {
+                label: '删除',
+                ifShow: getCheckPerm('agent-deal') && record.isSystem == 0,
+                color: 'error',
+                onClick: handleDelete.bind(null, record),
+              },
+            ]"
+          />
+        </template>
+      </BasicTable>
+    </div>
+    <addconfigModal :typeId="state" @register="register" @update="reload" />
+  </PageWrapper>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, h } from 'vue';
+  import { BasicTable, useTable, TableAction, FormProps } from '/@/components/Table';
+  import { PageWrapper } from '/@/components/Page';
+  import { Divider, Card, Empty, Descriptions, Steps, Tabs } from 'ant-design-vue';
+  import { getAllByTypeId, feedbackOptionList, feedbackOptiondelete } from '/@/api/operate';
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import addconfigModal from './components/config/addModal.vue';
+  import { useModal } from '/@/components/Modal';
+  import { configurationSchema, refundTimeTableData } from './data';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { useRouter } from 'vue-router';
+  import { usePermissionStore } from '/@/store/modules/permission';
+  export default defineComponent({
+    components: {
+      BasicTable,
+      PageWrapper,
+      TableAction,
+      addconfigModal,
+      [Divider.name]: Divider,
+      [Card.name]: Card,
+      Empty,
+      [Descriptions.name]: Descriptions,
+      [Descriptions.Item.name]: Descriptions.Item,
+      [Steps.name]: Steps,
+      [Steps.Step.name]: Steps.Step,
+      [Tabs.name]: Tabs,
+      [Tabs.TabPane.name]: Tabs.TabPane,
+    },
+    setup() {
+      const { t } = useI18n();
+      const router = useRouter();
+      const [register, { openModal }] = useModal();
+      const permissionStore = usePermissionStore();
+      const { getCheckPerm } = permissionStore;
+      const { createMessage, createConfirm } = useMessage();
+      const state = ref<number>(1); //未处理,0已处理(默认1)
+      const searchForm: Partial<FormProps> = {
+        labelWidth: 100,
+        autoSubmitOnEnter: true,
+        schemas: [
+          {
+            field: 'payTime',
+            label: '提交时间',
+            component: 'RangePicker',
+            componentProps: {
+              maxLength: 100,
+              format: 'YYYY-MM-DD',
+              valueFormat: 'YYYY-MM-DD',
+              // showTime: true,
+            },
+            colProps: {
+              xl: 11,
+              xxl: 11,
+            },
+          },
+          {
+            field: 'companyName',
+            label: '公司名称',
+            component: 'Input',
+            colProps: {
+              xl: 6,
+              xxl: 6,
+            },
+          },
+        ],
+      };
+      const [registerTimeTable, { reload }] = useTable({
+        api: feedbackOptionList,
+        // title: '',
+        rowKey: 'id',
+        fetchSetting: {
+          pageField: 'pageNum',
+          sizeField: 'pageSize',
+          listField: 'list',
+          totalField: 'total',
+        },
+        searchInfo: {
+          typeId: state,
+        },
+        columns: configurationSchema,
+        // formConfig: searchForm,
+        useSearchForm: false,
+        dataSource: refundTimeTableData,
+        showTableSetting: true,
+        showIndexColumn: false,
+        // pagination: { pageSize: 20 },
+        // scroll: { y: 300 },
+        actionColumn: {
+          width: 100,
+          title: '操作',
+          // ifShow:state.value == 1,
+          dataIndex: 'action',
+          slots: { customRender: 'action' },
+        },
+      });
+      function changeTable(val: string) {
+        state.value = val;
+        reload();
+      }
+      async function handleWithdraw(record: Recordable) {
+        openModal(true, record);
+      }
+      function handleEidt(record) {
+        openModal(true, record);
+      }
+      async function handleDelete(record: Recordable) {
+        createConfirm({
+          iconType: 'warning',
+          title: () => h('span', '温馨提示'),
+          content: () => h('span', '确定要删除吗?'),
+          onOk: async () => {
+            await feedbackOptiondelete({ id: record.id });
+            reload();
+            createMessage.success(t('common.optSuccess'));
+          },
+        });
+      }
+      return {
+        registerTimeTable,
+        handleWithdraw,
+        changeTable,
+        state,
+        reload,
+        register,
+        openModal,
+        getCheckPerm,
+        handleEidt,
+        handleDelete,
+        router,
+      };
+    },
+  });
+</script>

+ 29 - 15
src/views/operate/data.tsx

@@ -24,7 +24,12 @@ export const refundTimeTableSchema: BasicColumn[] = [
     width: 150,
     dataIndex: 'state',
     customRender: ({ record }) => {
-      return <Badge status={record.state==1?'warning':'success'} text={record.state == 1?'待处理':'已处理'} />;
+      return (
+        <Badge
+          status={record.state == 1 ? 'warning' : 'success'}
+          text={record.state == 1 ? '待处理' : '已处理'}
+        />
+      );
     },
   },
   {
@@ -33,7 +38,18 @@ export const refundTimeTableSchema: BasicColumn[] = [
     dataIndex: 'noteContent',
   },
 ];
-
+export const configurationSchema: BasicColumn[] = [
+  {
+    title: '选项(中文)',
+    width: 400,
+    dataIndex: 'nameCn',
+  },
+  {
+    title: '选项(英文)',
+    width: 400,
+    dataIndex: 'nameEn',
+  },
+];
 export const agentSchema: BasicColumn[] = [
   {
     title: '公司名称',
@@ -50,7 +66,7 @@ export const agentSchema: BasicColumn[] = [
     width: 150,
     dataIndex: 'country',
     customRender: ({ record }) => {
-      return <Badge status='success' text={record.type == 1?'线下':'线上'} />;
+      return <Badge status="success" text={record.type == 1 ? '线下' : '线上'} />;
     },
   },
   {
@@ -64,7 +80,7 @@ export const agentSchema: BasicColumn[] = [
     dataIndex: 'surName',
     ellipsis: true,
     customRender: ({ record }) => {
-      return record.surName + record.userName
+      return record.surName + record.userName;
     },
   },
   {
@@ -103,14 +119,14 @@ export const agentSchema: BasicColumn[] = [
     dataIndex: 'noteContent',
   },
 ];
-let industryListObj = {
-  1:'测绘或激光扫描',
-  2:'建筑、工程和施工',
-  3:'石油和天然气',
-  4:'软件和IT服务',
-  5:'学术界',
-  6:'其他',
-}
+const industryListObj = {
+  1: '测绘或激光扫描',
+  2: '建筑、工程和施工',
+  3: '石油和天然气',
+  4: '软件和IT服务',
+  5: '学术界',
+  6: '其他',
+};
 export const DMegaSchema: BasicColumn[] = [
   {
     title: '公司名称',
@@ -122,9 +138,7 @@ export const DMegaSchema: BasicColumn[] = [
     width: 150,
     dataIndex: 'job',
     customRender: ({ record }) => {
-      return (
-        record.job &&industryListObj[record.job]
-      );
+      return record.job && industryListObj[record.job];
     },
   },
   {

+ 203 - 106
src/views/operate/userFeedback.vue

@@ -1,34 +1,38 @@
 <template>
   <div>
-    <div class="score m-4 p-4 bg-white">
-      <div class="scoreTitle text-base">硬件产品评分</div>
-      <div class=""></div>
+    <div class="score m-4 bg-white">
+      <a-collapse v-model:activeKey="activeKey" ghost :bordered="false">
+        <a-collapse-panel key="1" header="硬件产品评分" ghost :bordered="false">
+          <GrowCard :loading="loading" class="enter-y" :list="score.hardware" />
+        </a-collapse-panel>
+        <a-collapse-panel key="2" header="软件产品评分">
+          <GrowCard :loading="loading" class="enter-y" :list="score.hardware" />
+        </a-collapse-panel>
+      </a-collapse>
+      <!-- <div class="scoreTitle text-base">硬件产品评分</div>
+      <div class="scoreList">
+        <GrowCard :loading="loading" class="enter-y" :list="score.hardware" />
+      </div>
+      <div class="scoreTitle text-base">软件产品评分</div>
+      <div class="scoreList">
+        <GrowCard :loading="loading" class="enter-y" :list="score.hardware" />
+      </div> -->
     </div>
     <BasicTable @register="registerTable" @editEnd="editEnd">
       <template #toolbar>
-        <a-button type="primary" v-if="getCheckPerm('recruit-add')" @click="openModal(true)">
-          新增职位</a-button
-        >
+        <a-button type="primary" v-if="getCheckPerm('recruit-add')" @click="router.push('/operate/configuration')"> 选项配置 </a-button>
+        <a-button type="primary" v-if="getCheckPerm('recruit-add')" @click="downTemplate({})"> 导出 </a-button>
       </template>
       <template #action="{ record }">
         <TableAction
           stopButtonPropagation
           :actions="[
             {
-              label: '编辑',
+              label: record.status == 0 ? '处理' : '查看',
               //icon: 'ep:edit',
-              ifShow: getCheckPerm('recruit-edit') && record.isPush == 0,
+              //ifShow: record.state == 0,
               onClick: handleEdit.bind(null, record),
             },
-            {
-              label: '删除',
-              ifShow: getCheckPerm('recruit-delete'),
-              //icon: 'ic:outline-delete-outline',
-              popConfirm: {
-                title: '是否确认删除',
-                confirm: handleDelete.bind(null, record),
-              },
-            },
           ]"
         />
       </template>
@@ -37,24 +41,29 @@
   </div>
 </template>
 <script lang="ts">
-  import { defineComponent, h } from 'vue';
+  import { defineComponent, h, ref, onMounted, reactive } from 'vue';
   import { BasicTable, useTable, TableAction, BasicColumn, FormProps } from '/@/components/Table';
   import { Time } from '/@/components/Time';
   import {
-    employNoteList,
-    addOrUpdate,
+    getAllByTypeId,
+    getScoreAug,
+    getfeedbackList,
     employNoteIsTop,
     NewPublicNews,
     employNoteIsPush,
     employNoteDelete,
+    downTemplate,
   } from '/@/api/operate';
   import { useModal } from '/@/components/Modal';
+  import GrowCard from './components/GrowCard.vue';
   import { Descriptions } from 'ant-design-vue';
   import { useI18n } from '/@/hooks/web/useI18n';
   import { useMessage } from '/@/hooks/web/useMessage';
   import { Switch } from 'ant-design-vue';
-  import addRecruitModal from './components/recruit/addModal.vue';
+  import addRecruitModal from './components/config/messgeModal.vue';
+  import { useRouter } from 'vue-router';
   import { usePermissionStore } from '/@/store/modules/permission';
+  import { Collapse } from 'ant-design-vue';
   export default defineComponent({
     components: {
       BasicTable,
@@ -62,6 +71,10 @@
       addRecruitModal,
       [Descriptions.name]: Descriptions,
       [Descriptions.Item.name]: Descriptions.Item,
+      GrowCard,
+      [Collapse.name]: Collapse,
+      [Collapse.Panel.name]: Collapse.Panel,
+      
     },
     setup() {
       const { t } = useI18n();
@@ -69,42 +82,82 @@
       const permissionStore = usePermissionStore();
       const { getCheckPerm } = permissionStore;
       const [register, { openModal }] = useModal();
+      const router = useRouter()
+      const loading = ref(true);
+      const score = ref({
+        hardware: [],
+        software: [],
+      });
+      const activeKey = ref([]);
+      const fileFlow = reactive({
+        file: null,
+      });
       const columns: BasicColumn[] = [
         {
-          title: '职位名称',
-          dataIndex: 'workName',
+          title: '问题描述',
+          dataIndex: 'problemDesc',
           ellipsis: true,
+          align: 'left',
           width: 250,
         },
         {
-          title: '工作地点',
-          dataIndex: 'workAddress',
+          title: '硬件产品',
+          dataIndex: 'hardwareOption',
           ellipsis: true,
           width: 150,
+          customRender: ({ record }) => {
+            return record.hardwareOption?.nameCn;
+          },
         },
         {
-          title: '薪资待遇',
-          dataIndex: 'salary',
+          title: '软件产品',
+          dataIndex: 'softwareOption',
           ellipsis: false,
-          width: 80,
+          width: 200,
+          customRender: ({ record }) => {
+            return record.softwareOption?.nameCn;
+          },
         },
         {
-          title: '招聘人数',
-          dataIndex: 'employNum',
+          title: '所在行业',
+          dataIndex: 'industryOption',
           width: 150,
-          edit: true,
-          editRule: async (text) => {
-            if (text < 1) {
-              return '招聘人数应大于0';
-            } else if (text > 999) {
-              return '招聘人数应小于999';
-            }
-            return '';
+          customRender: ({ record }) => {
+            return record.industryOption?.nameCn;
           },
-          editComponent: 'InputNumber',
         },
         {
-          title: '创建时间',
+          title: '期望解决方案',
+          dataIndex: 'solution',
+          width: 150,
+        },
+        {
+          title: '姓名',
+          dataIndex: 'nickName',
+          width: 80,
+        },
+        {
+          title: '联系方式',
+          dataIndex: 'phone',
+          width: 120,
+        },
+        {
+          title: '国家和地区',
+          dataIndex: 'address',
+          width: 120,
+        },
+        {
+          title: '评分',
+          dataIndex: 'score',
+          width: 50,
+        },
+        {
+          title: '评分理由',
+          dataIndex: 'scoreReason',
+          width: 150,
+        },
+        {
+          title: '反馈时间',
           dataIndex: 'createTime',
           width: 150,
           customRender: ({ record }) => {
@@ -118,91 +171,107 @@
           },
         },
         {
-          title: '状态',
-          dataIndex: 'isPush',
+          title: '处理状态',
+          dataIndex: 'status',
           width: 80,
-          ifShow: getCheckPerm('recruit-publish'),
           customRender: ({ record }) => {
-            if (!Reflect.has(record, 'pendingStatus')) {
-              record.pendingStatus = false;
-            }
-            return h(Switch, {
-              checked: record.isPush === 1,
-              checkedChildren: '已发布',
-              unCheckedChildren: '未发布',
-              loading: false,
-              onChange: async (checked: boolean) => {
-                record.pendingStatus = true;
-                const id: string = record.id || '';
-                const newStatus = checked ? 1 : 0;
-                Reflect.set(record, 'isPush', newStatus);
-                await employNoteIsPush({ id: id, isPush: newStatus });
-                createMessage.success(t('common.optSuccess'));
-                // reload()
-              },
-            });
+            return record.status == 0 ? '未处理' : '已处理';
           },
         },
         {
-          title: '置顶',
-          dataIndex: 'isTop',
-          ifShow: getCheckPerm('recruit-top'),
-          width: 80,
+          title: '处理结果',
+          dataIndex: 'result',
+          width: 120,
           customRender: ({ record }) => {
-            if (!Reflect.has(record, 'pendingStatus')) {
-              record.pendingStatus = false;
-            }
-            return h(Switch, {
-              checked: record.isTop === 1,
-              checkedChildren: '是',
-              unCheckedChildren: '否',
-              loading: false,
-              onChange: async (checked: boolean) => {
-                record.pendingStatus = true;
-                const id: string = record.id || '';
-                const newStatus = checked ? 1 : 0;
-                // Reflect.set(record, 'isOnSale', newStatus);
-                await employNoteIsTop({ id, isTop: newStatus });
-                createMessage.success(t('common.optSuccess'));
-                reload();
-              },
-            });
+            return record.result || '-';
           },
         },
       ];
       const searchForm: Partial<FormProps> = {
         labelWidth: 100,
         autoSubmitOnEnter: true,
+        autoAdvancedLine: 1,
         schemas: [
-          // {
-          //   field: 'sceneName',
-          //   label: t('routes.operate.releaseTime'),
-          //   component: 'RangePicker',
-          //   componentProps: {
-          //     maxLength: 100,
-          //     format: 'YYYY-MM-DD',
-          //     valueFormat:'YYYY-MM-DD',
-          //     showTime: true,
-          //   },
-          //   colProps: {
-          //     xl: 11,
-          //     xxl: 11,
-          //   },
-          // },
           {
-            field: 'workName',
-            label: '职位名称',
-            component: 'Input',
+            field: 'hardwareOptionId',
+            component: 'ApiSelect',
+            label: '硬件产品',
+            itemProps: {
+              validateTrigger: 'blur',
+            },
+            componentProps: {
+              api: getAllByTypeId,
+              numberToString: true,
+              labelField: 'nameCn',
+              valueField: 'id',
+              immediate: true,
+              params: 2,
+            },
             colProps: {
               xl: 6,
-              xxl: 6,
+              xxl: 8,
+            },
+          },
+          {
+            field: 'softwareOptionId',
+            component: 'ApiSelect',
+            label: '软件产品',
+            itemProps: {
+              validateTrigger: 'blur',
+            },
+            componentProps: {
+              api: getAllByTypeId,
+              numberToString: true,
+              labelField: 'nameCn',
+              valueField: 'id',
+              immediate: true,
+              params: 3,
+            },
+            colProps: {
+              xl: 6,
+              xxl: 8,
+            },
+          },
+          {
+            field: 'industryOptionId',
+            component: 'ApiSelect',
+            label: '所在行业',
+            itemProps: {
+              validateTrigger: 'blur',
+            },
+            componentProps: {
+              api: getAllByTypeId,
+              numberToString: true,
+              labelField: 'nameCn',
+              valueField: 'id',
+              immediate: true,
+              params: 1,
+            },
+            colProps: {
+              xl: 6,
+              xxl: 8,
+            },
+          },
+          {
+            field: 'status',
+            label: '处理状态',
+            component: 'Select',
+            componentProps: {
+              options: [
+                { label: '未处理', value: '0' },
+                { label: '已处理', value: '1' },
+              ],
+            },
+            colProps: {
+              xl: 8,
+              xxl: 8,
             },
           },
         ],
       };
       const [registerTable, { reload }] = useTable({
-        api: employNoteList,
-        title: '职位列表',
+        api: getfeedbackList,
+        title: '用户反馈列表',
         columns: columns,
         useSearchForm: true,
         formConfig: searchForm,
@@ -223,6 +292,12 @@
         },
         canResize: true,
       });
+      onMounted(() => {
+        getScoreAug().then(res => {
+          score.value = res
+          loading.value = false
+        })
+      });
       async function handleDelete(record: Recordable) {
         await employNoteDelete({ id: record.id });
         createMessage.success(t('common.optSuccess'));
@@ -235,7 +310,6 @@
         reload();
       }
       function handleEdit(record: Recordable) {
-        console.log('点击了编辑', record);
         openModal(true, record);
       }
       async function handleWithdraw(record: Recordable) {
@@ -263,14 +337,37 @@
         openModal,
         editEnd,
         getCheckPerm,
+        score,
+        activeKey,
+        loading,
+        router,
+        downTemplate,
       };
     },
   });
 </script>
 <style lang="less" scoped>
   .score {
-    mar
     .scoreTitle {
+      margin: 10px 0;
+    }
+    .scoreList{
+      // display: flex;
+      width: 100%;
+      overflow: auto;
+      .scoreitem {
+        // 保障flex下元素宽度不变
+        flex-grow: 0;
+        flex-shrink: 0;
+        padding: 10px 20px 0 20px;
+        margin-right: 20px;
+        width: calc(100% / 6 - 20px);
+        border: 1px solid #444
+      }
+    }
+    ::v-deep .ant-collapse-item{
+      background: #fff;
+      border: none;
     }
   }
 </style>