浏览代码

PDF报告

jwyu 9 月之前
父节点
当前提交
070ce4b8ed

+ 0 - 4
src/api/customer/modules/user.js

@@ -35,10 +35,6 @@ export default {
   userDelete:params=>{
     return post('/user/delete',params)
   },
-  // 报告分类
-  reportClassify:()=>{
-    return get('/classify/list',{})
-  },
   // 潜在用户列表
   potentialUserList:params=>{
     return get('/user/potential/list',params)

+ 12 - 0
src/api/report/common.js

@@ -0,0 +1,12 @@
+import { get, post } from "@/api/index";
+
+export default {
+  //报告分类
+  classifyList: () => {
+    return get('/classify/list',{})
+  },
+  // 研报作者
+  reportAuthorList:()=>{
+    return get('/report_pdf/author',{})
+  }
+};

+ 7 - 0
src/api/report/index.js

@@ -0,0 +1,7 @@
+import apiReportCommon from './common'
+import apiReportPdf from './pdf'
+
+export {
+  apiReportCommon,
+  apiReportPdf,
+}

+ 28 - 0
src/api/report/pdf.js

@@ -0,0 +1,28 @@
+import { get, post } from "@/api/index";
+
+export default {
+  //新增pdf报告
+  reportPdfAdd: params => {
+    return post('/report_pdf/add',params)
+  },
+  //编辑pdf报告
+  reportPdfEdit: params => {
+    return post('/report_pdf/edit',params)
+  },
+  //pdf报告列表
+  reportPdfList: params => {
+    return get('/report_pdf/list',params)
+  },
+  //pdf报告发布
+  reportPdfPubllish: params => {
+    return get('/report_pdf/publish',params)
+  },
+  //pdf报告取消发布
+  reportPdfCancelPubllish: params => {
+    return get('/report_pdf/publishCancel',params)
+  },
+  //pdf报告删除
+  reportPdfDelete: params => {
+    return get('/report_pdf/delete',params)
+  },
+};

+ 4 - 0
src/api/system/common.js

@@ -8,5 +8,9 @@ export default{
     // 手机号区号
     phoneAreaCode:()=>{
         return get('/sys_user/area_code/list',{})
+    },
+    // 上传文件
+    uploadFile:params=>{
+        return post('/report_pdf/uploadPdf',params)
     }
 }

+ 2 - 2
src/components/SelectReportClassify.vue

@@ -1,5 +1,5 @@
 <script setup>
-import { apiCustomerUser } from '@/api/customer'
+import { apiReportCommon } from '@/api/report'
 
 const model=defineModel()
 const props=defineProps({
@@ -13,7 +13,7 @@ const emits=defineEmits(['change'])
 
 const options=ref([])
 async function getData(){
-  const res=await apiCustomerUser.reportClassify()
+  const res=await apiReportCommon.classifyList()
   if(res.Ret!=200) return
   const arr=res.Data||[]
   options.value=arr

+ 86 - 0
src/components/UploadFile.vue

@@ -0,0 +1,86 @@
+<script setup>
+import { apiSystemCommon } from '@/api/system'
+import { ElMessage } from 'element-plus'
+
+const props=defineProps({
+  showFileList:{
+    type:Boolean,
+    default:false
+  },
+  fileSize:{//上传大小控制 -1则不控制 以M为单位
+    type:Number,
+    default:-1,
+  },
+  accept:{//多个用英文逗号隔开 eg:.doc,.docx,
+    type:String,
+    default:''
+  },
+  autoUpload:{
+    type:Boolean,
+    default:true
+  }
+})
+
+const emits=defineEmits(['success'])
+
+function beforeUpload(e){
+  // console.log(e);
+  // 校验文件类型
+  if(props.accept){
+    const typeArr=props.accept.split(',')
+    if(!typeArr.includes(e.type)){
+      ElMessage.warning('请上传正确的文件类型')
+      return false
+    }
+  }
+  // 校验文件大小
+  if(props.fileSize!==-1){
+    const acceptSize=props.fileSize*1024*1024
+    if(acceptSize<e.size){
+      ElMessage.warning('文件过大')
+      return false
+    }
+  }
+
+  return true
+}
+
+const loading=ref(false)
+async function handleUpload(e){
+  const form = new FormData()
+  form.append("File", e.file)
+  loading.value=true
+  const res=await apiSystemCommon.uploadFile(form)
+  loading.value=false
+  if(res.Ret===200){
+    emits('success',res.Data)
+  }
+}
+
+
+</script>
+
+<template>
+  <el-upload
+    ref="uploadRef"
+    action=""
+    :auto-upload="props.autoUpload"
+    :showFileList="props.showFileList"
+    :accept="props.accept"
+    :before-upload="beforeUpload"
+    :http-request="handleUpload"
+  >
+    
+    <template #trigger>
+      <div v-loading="loading" element-loading-text="上传中...">
+        <slot name="trigger"></slot>
+      </div>
+    </template>
+    
+    <slot></slot>
+    
+    <template #tip>
+      <slot name="tip"></slot>
+    </template>
+  </el-upload>
+</template>

+ 22 - 0
src/router/modules/report.js

@@ -0,0 +1,22 @@
+import LayoutIndex from '@/layout/Index.vue'
+
+export default[
+  {
+    path:'/report',
+    component:LayoutIndex,
+    name:'ReportIndex',
+    meta:{
+      title:'PDF报告'
+    },
+    children:[
+      {
+        path:'pdfList',
+        component:()=>import('@/views/report/pdf/List.vue'),
+        name:"ReportPdfList",
+        meta:{
+          title:'PDF报告'
+        },
+      },
+    ]
+  }
+]

+ 339 - 0
src/views/report/pdf/List.vue

@@ -0,0 +1,339 @@
+<script setup>
+import { Search, Plus } from '@element-plus/icons-vue'
+import EditPdf from './components/EditPdf.vue'
+import { apiReportPdf } from '@/api/report'
+import { dayjs, ElMessage, ElMessageBox } from 'element-plus'
+
+const filterState = reactive({
+  keyword: '',
+  classifyIds: '',
+  status: '',
+  publishTime: [],
+  updateTime: [],
+  sortType:'',
+  sortVal:''
+})
+
+const tableColOpt = [
+  {
+    label: '报告标题',
+    key: 'Title'
+  },
+  {
+    label: '类型',
+    key: 'ClassifyNameFirst'
+  },
+  {
+    label: '作者',
+    key: 'Author'
+  },
+  {
+    label: '摘要',
+    key: 'Abstract'
+  },
+  {
+    label: '状态',
+    key: 'State',
+    width: '100px'
+  },
+  {
+    label: '发布时间',
+    key: 'PublishTime',
+    sort: true,
+    width: '200px'
+  },
+  {
+    label: '更新时间',
+    key: 'ModifyTime',
+    sort: true,
+    width: '200px'
+  },
+  {
+    label: '期数',
+    key: 'Stage',
+    width: '100px'
+  },
+  {
+    label: 'PV/UV',
+    key: 'Pv',
+    headerTips: 'pv:报告被打开的次数,每次打开都计算一次<br> uv:访问报告的人数,每篇报告同一个人访问只计算一次'
+  }
+]
+function getTitleTime(e) {
+  if (e.PublishTime) {
+    return dayjs(e.PublishTime).format('MMDD')
+  }
+  return dayjs(e.ModifyTime).format('MMDD')
+}
+
+const userList = ref([])
+const page = ref(1)
+const pageSize = ref(10)
+const tableLoading = ref(false)
+const totals = ref(0)
+async function getReportList() {
+  tableLoading.value = true
+  const res = await apiReportPdf.reportPdfList({
+    PageSize: pageSize.value,
+    CurrentIndex: page.value,
+    ClassifyIds:filterState.classifyIds ? filterState.classifyIds.join(',') : '',
+    State:filterState.status,
+    PublishStartDate:filterState.publishTime ? filterState.publishTime[0] : '',
+    PublishEndDate:filterState.publishTime ? filterState.publishTime[1] : '',
+    ModifyStartDate:filterState.updateTime ? filterState.updateTime[0] : '',
+    ModifyEndDate:filterState.updateTime ? filterState.updateTime[1] : '',
+    KeyWord:filterState.keyword,
+    SortParam:filterState.sortType,
+    SortType:filterState.sortVal
+  })
+  tableLoading.value = false
+  if (res.Ret === 200) {
+    userList.value = res.Data.List || []
+    totals.value = res.Data.Paging.Totals
+  }
+}
+getReportList()
+function handlePageChange(e) {
+  page.value = e
+  getReportList()
+}
+function handleTableSort(e) {
+  // console.log(e);
+  const { order, prop } = e//order:"descending",prop: "RegisterTime"
+  filterState.sortType = prop
+  if (!order) {
+    filterState.sortVal = ''
+  } else {
+    filterState.sortVal = order === 'descending' ? 'desc' : 'asc'
+  }
+
+  handleFilterList()
+}
+function handleFilterList() {
+  page.value = 1
+  getReportList()
+}
+
+
+const showEdit = ref(false)
+const editData = ref(null)
+function handleEditPdf(e){
+  editData.value=e
+  showEdit.value=true
+}
+
+async function handleReportCancelPublish(e){
+  await ElMessageBox.confirm('是否确认撤销改报告','提示')
+  const res=await apiReportPdf.reportPdfCancelPubllish({
+    ReportPdfId:e.ReportPdfId
+  })
+  if(res.Ret===200){
+    ElMessage.success('撤销成功')
+    handleFilterList()
+  }
+}
+
+async function handleReportPublish(e){
+  const res=await apiReportPdf.reportPdfPubllish({
+    ReportPdfId:e.ReportPdfId
+  })
+  if(res.Ret===200){
+    ElMessage.success('发布成功')
+    handleFilterList()
+  }
+}
+
+async function handleReportDelete(e){
+  await ElMessageBox.confirm('报告删除后无法恢复,确认删除吗?','提示')
+  const res=await apiReportPdf.reportPdfDelete({
+    ReportPdfId:e.ReportPdfId
+  })
+  if(res.Ret===200){
+    ElMessage.success('删除成功')
+    handleFilterList()
+  }
+}
+
+</script>
+
+<template>
+  <div class="report-pdf-list-page">
+    <div>
+      <el-button v-permission="'reportPdf:add'" type="primary" :icon="Plus" @click="showEdit = true;editData=null"
+        >上传报告</el-button
+      >
+      <el-input
+        placeholder="报告标题/创建人"
+        v-model="filterState.keyword"
+        :prefix-icon="Search"
+        clearable
+        style="max-width: 359px; float: right"
+        @input="handleFilterList"
+      />
+    </div>
+    <div class="flex filter-wrap">
+      <select-report-classify
+        v-model="filterState.classifyIds"
+        clearable
+        :props="{
+          emitPath: false,
+          multiple: true,
+        }"
+        @change="handleFilterList"
+      />
+      <el-select
+        placeholder="状态"
+        v-model="filterState.status"
+        style="width: 165px"
+        clearable
+        @change="handleFilterList"
+      >
+        <el-option label="已发布" :value="1"></el-option>
+        <el-option label="未发布" :value="2"></el-option>
+      </el-select>
+
+      <div style="width: 235px">
+        <el-date-picker
+          style="width: 235px"
+          v-model="filterState.publishTime"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="发布时间"
+          end-placeholder="发布时间"
+          clearable
+          value-format="YYYY-MM-DD"
+          @change="handleFilterList"
+        />
+      </div>
+      <div style="width: 235px">
+        <el-date-picker
+          style="width: 235px"
+          v-model="filterState.updateTime"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="更新时间"
+          end-placeholder="更新时间"
+          value-format="YYYY-MM-DD"
+          clearable
+          @change="handleFilterList"
+        />
+      </div>
+    </div>
+    <div class="list-wrap" style="margin-top: 20px">
+      <el-table
+        :data="userList"
+        border
+        stripe
+        highlight-current-row
+        element-loading-text="数据加载中..."
+        v-loading="tableLoading"
+        @sort-change="handleTableSort"
+      >
+        <el-table-column
+          v-for="column in tableColOpt"
+          :key="column.key"
+          :prop="column.key"
+          :label="column.label"
+          :sortable="column.sort ? 'custom' : false"
+          :width="column.width"
+        >
+          <template v-if="column.headerTips" #header>
+            <span>{{ column.label }}</span>
+            <el-tooltip
+              class="box-item"
+              effect="dark"
+              raw-content
+              :content="column.headerTips"
+              placement="top"
+            >
+              <el-icon style="position: relative; top: 2px"
+                ><i-ep-QuestionFilled
+              /></el-icon>
+            </el-tooltip>
+          </template>
+          <template #default="{ row }">
+            <span v-if="column.key === 'Title'"
+              >{{ row.Title }}({{ getTitleTime(row) }})</span
+            >
+            <span v-else-if="column.key === 'ClassifyNameFirst'"
+              >{{ row.ClassifyNameFirst }} / {{ row.ClassifyNameSecond }}</span
+            >
+            <span v-else-if="column.key === 'State'">{{
+              row.State === 1 ? "已发布" : "未发布"
+            }}</span>
+            <span v-else-if="column.key === 'PublishTime'">{{
+              formatTime(row.PublishTime)
+            }}</span>
+            <span v-else-if="column.key === 'ModifyTime'">{{
+              formatTime(row.ModifyTime)
+            }}</span>
+            <span v-else-if="column.key === 'Stage'">第{{ row.Stage }}期</span>
+            <span v-else-if="column.key === 'Pv'"
+              >{{ row.Pv }} / {{ row.Uv }}</span
+            >
+            <span v-else>{{ row[column.key] }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="160">
+          <template #default="{ row }">
+            <el-button
+              v-permission="'reportPdf:edit'"
+              type="primary"
+              link
+              @click="handleEditPdf(row)"
+              >编辑</el-button
+            >
+            <el-button
+              v-permission="'reportPdf:publishCancel'"
+              type="primary"
+              link
+              v-if="row.State===1"
+              @click="handleReportCancelPublish(row)"
+              >撤销</el-button
+            >
+            <el-button
+              v-permission="'reportPdf:publish'"
+              type="primary"
+              link
+              v-if="row.State===2"
+              @click="handleReportPublish(row)"
+              >发布</el-button
+            >
+            <el-button
+              v-permission="'rtPdf:delete'"
+              type="danger"
+              link
+              v-if="row.State===2"
+              @click="handleReportDelete(row)"
+              >删除</el-button
+            >
+            
+          </template>
+        </el-table-column>
+      </el-table>
+      <el-pagination
+        background
+        layout="total,prev,pager,next,jumper"
+        :current-page="page"
+        :page-size="pageSize"
+        :total="totals"
+        @current-change="handlePageChange"
+        style="margin-top: 30px; justify-content: flex-end"
+      />
+    </div>
+  </div>
+
+  <EditPdf v-model:show="showEdit" :defaultData="editData" />
+</template>
+
+<style lang="scss" scoped>
+.report-pdf-list-page {
+  width: 100%;
+  overflow: hidden;
+  .filter-wrap {
+    margin-top: 10px;
+    flex-wrap: wrap;
+    gap: 10px;
+  }
+}
+</style>

+ 162 - 0
src/views/report/pdf/components/EditPdf.vue

@@ -0,0 +1,162 @@
+<script setup>
+import { Plus, CircleClose } from '@element-plus/icons-vue'
+import SelectAuthor from './SelectAuthor.vue'
+import {apiReportPdf} from '@/api/report'
+import { ElMessage } from 'element-plus'
+
+const show = defineModel('show', { type: Boolean, default: false })
+
+const props=defineProps({
+  defaultData:{
+    type:[Object,null],
+    default:null
+  }
+})
+
+const formIns = ref(null)
+const FORM_RULES = {
+  title: [{ required: true, message: '请填写报告标题' }],
+  classify:[{ required: true, message: '请选择报告分类' }]
+}
+const formData = reactive({
+  fileUrl: '',
+  fileName: '',
+  title: "",
+  classify: '',
+  abstract: '',
+  author: ''
+})
+
+watch(()=>show.value,(n)=>{
+  if(n&&props.defaultData){
+    formData.fileUrl=props.defaultData.PdfUrl
+    formData.fileName=props.defaultData.PdfName
+    formData.title=props.defaultData.Title
+    formData.classify=[props.defaultData.ClassifyIdFirst,props.defaultData.ClassifyIdSecond]
+    formData.abstract=props.defaultData.Abstract
+    formData.author=props.defaultData.Author.split(',')
+  }else{
+    for(let key in formData){
+      formData[key]=''
+    }
+  }
+})
+
+function handleUploadSuccess(e) {
+  formData.fileUrl = e.Url
+  formData.fileName = e.FileName
+}
+
+async function onSubmit() {
+  await formIns.value.validate()
+  if(!formData.fileUrl) {
+    ElMessage.warning('请上传报告PDF文件')
+    return
+  }
+  const params={
+    PdfUrl:formData.fileUrl,
+    PdfName:formData.fileName,
+    Title:formData.title,
+    Author:formData.author?formData.author.join(','):'',
+    Abstract:formData.abstract,
+    ClassifyIdFirst:formData.classify?formData.classify[0]:0,
+    ClassifyIdSecond:formData.classify?formData.classify[1]||0:0,
+  }
+  const res=props.defaultData?await apiReportPdf.reportPdfEdit({
+    ReportPdfId:props.defaultData.ReportPdfId,
+    ...params,
+  }):await apiReportPdf.reportPdfAdd(params)
+
+  if(res.Ret===200){
+    ElMessage.success('保存成功')
+    show.value=false
+  }
+
+}
+
+</script>
+
+<template>
+  <el-dialog v-model="show" width="700" :title="props.defaultData?'编辑':'上传报告'" draggable>
+    <el-form
+      ref="formIns"
+      label-width="80px"
+      :rules="FORM_RULES"
+      :model="formData"
+      @submit="onSubmit"
+      class="form-wrap"
+    >
+      <upload-file
+        accept="application/pdf"
+        :fileSize="15"
+        @success="handleUploadSuccess"
+        class="upload-box"
+      >
+        <div v-if="formData.fileUrl" class="file-name-box">
+          <el-link type="primary" style="font-size:18px">{{ formData.fileName }}</el-link>
+          <el-icon color="#f00" size="20px" @click="formData.fileName='';formData.fileUrl=''"><CircleClose /></el-icon>
+        </div>
+        <template #trigger>
+          <el-button
+            class="upload-btn"
+            type="primary"
+            plain
+            :icon="Plus"
+            v-if="!formData.fileUrl"
+            >上传报告</el-button
+          >
+        </template>
+
+        <template #tip>
+          <div>要求文件格式为pdf,且不超过15M</div>
+        </template>
+      </upload-file>
+
+      <el-form-item label="报告标题" prop="title">
+        <el-input v-model="formData.title" placeholder="请输入报告标题" />
+      </el-form-item>
+      <el-form-item label="分类" prop="classify">
+        <select-report-classify style="width:100%" v-model="formData.classify"/>
+      </el-form-item>
+      <el-form-item label="分类" prop="abstract">
+        <el-input
+          type="textarea"
+          v-model="formData.abstract"
+          placeholder="请输入报告摘要"
+        />
+      </el-form-item>
+      <el-form-item label="作者" prop="author">
+        <SelectAuthor multiple v-model="formData.author" />
+      </el-form-item>
+    </el-form>
+
+    <div style="text-align: center">
+      <el-button type="primary" plain size="large" @click="show = false"
+        >取消</el-button
+      >
+      <el-button type="primary" size="large" @click="onSubmit">保存</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+.form-wrap {
+  width: 500px;
+  margin: 0 auto;
+}
+.upload-box {
+  text-align: center;
+  margin-bottom: 20px;
+  .file-name-box{
+    margin-bottom: 20px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 0 10px;
+  }
+  .upload-btn {
+    width: 500px;
+    margin-bottom: 10px;
+  }
+}
+</style>

+ 47 - 0
src/views/report/pdf/components/SelectAuthor.vue

@@ -0,0 +1,47 @@
+<script setup>
+import { apiReportCommon } from '@/api/report'
+
+const model = defineModel()
+const props = defineProps({
+  props: {},
+  clearable: {
+    type: Boolean,
+    default: false
+  },
+  multiple: {
+    type: Boolean,
+    default: false
+  },
+
+})
+const emits = defineEmits(['change'])
+
+const options = ref([])
+async function getData() {
+  const res = await apiReportCommon.reportAuthorList()
+  if (res.Ret != 200) return
+  const arr = res.Data || []
+  options.value = arr
+}
+getData()
+
+function handleChange() {
+  emits('change')
+}
+
+</script>
+<template>
+  <el-select
+    collapse-tags
+    :multiple="props.multiple"
+    v-model="model"
+    placeholder="请选择作者"
+  >
+    <el-option
+      v-for="item in options"
+      :key="item.Id"
+      :label="item.ReportAuthor"
+      :value="item.ReportAuthor"
+    />
+  </el-select>
+</template>