liyongli 1 рік тому
батько
коміт
fa9e58a1e1

+ 69 - 0
src/api/processing.js

@@ -0,0 +1,69 @@
+import ajax from '../utils/request.js';
+
+/**
+ * 审核图片
+ * @param {object} ori
+ * @returns
+ */
+export function storeImg(ori) {
+  return ajax({
+    api: '/review/img/store',
+    data: ori.data,
+    base: 'videoProcessing',
+    method: 'POST',
+    headers: {
+      Authorization: localStorage.getItem('token')
+    }
+  });
+}
+
+/**
+ * 图片历史记录
+ * @param {object} ori
+ * @returns
+ */
+export function historyImg(ori) {
+  return ajax({
+    api: '/review/img/list',
+    data: ori.data,
+    base: 'videoProcessing',
+    method: 'POST',
+    headers: {
+      Authorization: localStorage.getItem('token')
+    }
+  });
+}
+
+/**
+ * 审核视频
+ * @param {object} ori
+ * @returns
+ */
+export function storeVideo(ori) {
+  return ajax({
+    api: '/review/video/store',
+    data: ori.data,
+    base: 'videoProcessing',
+    method: 'POST',
+    headers: {
+      Authorization: localStorage.getItem('token')
+    }
+  });
+}
+
+/**
+ * 视频历史记录
+ * @param {object} ori
+ * @returns
+ */
+export function historyVideo(ori) {
+  return ajax({
+    api: '/review/video/list',
+    data: ori.data,
+    base: 'videoProcessing',
+    method: 'POST',
+    headers: {
+      Authorization: localStorage.getItem('token')
+    }
+  });
+}

Різницю між файлами не показано, бо вона завелика
+ 1 - 0
src/config/index.js


+ 22 - 0
src/router/allMedia.js

@@ -118,6 +118,28 @@ export default [
         component: () =>
           import(/* webpackChunkName: "RoleManage" */ '../view/allMedia/RoleManage.vue'),
       },
+      {
+        path: 'videoProcessing',
+        component: () =>
+          import(/* webpackChunkName: "videoProcessing" */ '../view/allMedia/videoProcessing/index.vue'),
+      },
+      {
+        path: 'videoProcessing_detail',
+        name: 'videoProcessing_detail',
+        component: () =>
+          import(/* webpackChunkName: "videoProcessing" */ '../view/allMedia/videoProcessing/detail.vue'),
+      },
+      {
+        path: 'imageProcessing',
+        component: () =>
+          import(/* webpackChunkName: "imageProcessing" */ '../view/allMedia/imageProcessing/index.vue'),
+      },
+      {
+        path: 'imageProcessing_detail',
+        name: 'imageProcessing_detail',
+        component: () =>
+          import(/* webpackChunkName: "imageProcessing" */ '../view/allMedia/imageProcessing/detail.vue'),
+      },
     ],
   },
 ];

+ 16 - 0
src/utils/request.js

@@ -10,6 +10,7 @@ function ajax(longRange) {
     if (window.XMLHttpRequest) xhttp = new XMLHttpRequest();
     else if (window.ActiveXObject)
       xhttp = new window.ActiveXObject('Microsoft.XMLHTTP');
+    
     var method = (longRange.method || 'GET').toUpperCase();
     if (method === 'GET') url += getdata(longRange.data || {});
     xhttp.open(method, url, true);
@@ -135,6 +136,11 @@ function noLogin(api = '#/login') {
   else location.href = origins[0] + api;
 }
 
+/**
+ * get请求参数
+ * @param {Object} body 
+ * @returns 
+ */
 function bodyGetFunc(body = {}) {
   let search = '?';
   for (const key in body) {
@@ -145,6 +151,11 @@ function bodyGetFunc(body = {}) {
   return search;
 }
 
+/**
+ * post请求参数
+ * @param {Object} body 
+ * @returns 
+ */
 function bodyFunc(body = {}) {
   const bodys = {};
   return JSON.stringify({
@@ -153,6 +164,11 @@ function bodyFunc(body = {}) {
   });
 }
 
+/**
+ * 添加头部
+ * @param {Object} headers 
+ * @returns 
+ */
 function headerFunc(headers = {}) {
   const head = {
     'Content-Type': 'application/json',

+ 129 - 0
src/view/allMedia/imageProcessing/detail.vue

@@ -0,0 +1,129 @@
+<template>
+  <div>
+    <header_local />
+    <div class="main">
+      <el-row class="head">
+        <el-col :span="12"> 图片审核 </el-col>
+      </el-row>
+      <br />
+      <el-row class="body">
+        <el-col
+          ref="left"
+          :span="16"
+          style="border-right: 2px solid #e4e4e4; padding: 0 26px"
+        >
+          <div v-text="detail.title" style="margin-bottom: 1em"></div>
+          <el-image
+            :style="
+              'width: ' +
+              image_data.width +
+              'px; height: ' +
+              image_data.height +
+              'px; margin-bottom: 3em'
+            "
+            :src="detail.url"
+            fit="contain"
+          />
+          <el-button
+            :type="status[detail.reviewResult.label]"
+            style="margin: 0 auto; display: block"
+            v-text="detail.reviewResult.labelDesc"
+          ></el-button>
+          <div style="text-align: center; height: 3em; line-height: 3em">
+            该图片共有{{ detail_data.total }}处违规
+          </div>
+        </el-col>
+        <el-col :span="8" style="padding: 0 17px">
+          <div style="height: 3em; line-height: 3em">审核结果</div>
+          <el-tabs
+            v-if="detail && detail.reviewResult && detail.reviewResult.results"
+            v-model="activeName"
+          >
+            <el-tab-pane
+              :label="item.typeDesc"
+              :name="item.type"
+              v-for="item in detail.reviewResult.results"
+              :key="item.type"
+            >
+              <div
+                class="err_item"
+                v-for="(p, index) in item.items"
+                :key="index"
+              >
+                <div class="err_item_label">
+                  {{ `${index + 1}、 类型:${p.labelDesc},错误内容:${p.subTypeDesc}` }}
+                </div>
+              </div>
+            </el-tab-pane>
+          </el-tabs>
+        </el-col>
+      </el-row>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import header_local from '../components/header.vue';
+// import { storeImg, historyImg } from '@/api/processing';
+import { ref, onMounted } from 'vue';
+// import config from '../../../config/index';
+const status = {
+  REJECT: 'danger',
+  REVIEW: 'warning',
+  NORMAL: 'success'
+};
+const left = ref(null);
+const image_data = ref({
+  height: 0,
+  width: 0
+});
+const detail = JSON.parse(
+  sessionStorage.getItem('Processing_detail') || '{reviewResult: {}}'
+);
+const activeName = ref(
+  detail && detail.reviewResult && detail.reviewResult.results
+    ? detail.reviewResult.results[0].type
+    : ''
+);
+
+const detail_data = ref({
+  total: 0
+});
+detail?.reviewResult?.results?.map(v => {
+  detail_data.value.total += v.items ? v.items.length : 0;
+});
+
+onMounted(() => {
+  const target = detail.reviewResult.thumbnailMeta;
+  const r_width = left.value.$el.offsetWidth - 52;
+  let h = (target.naturalHeight / target.naturalWidth) * r_width;
+  image_data.value = {
+    height: h,
+    width: r_width
+  };
+});
+
+console.log(detail);
+
+// const Authorization = localStorage.getItem('token') || '';
+</script>
+
+<style scoped>
+.main {
+  padding: 1em;
+  font-size: 14px;
+  font-family: PingFangSC, PingFang SC;
+  height: calc(100% - 42px);
+}
+
+.main .head {
+  background: #f9fafc;
+  height: 42px;
+  line-height: 42px;
+  padding: 0 22px;
+}
+
+.main .body {
+  height: calc(100% - 42px - 1em);
+}
+</style>

+ 191 - 0
src/view/allMedia/imageProcessing/index.vue

@@ -0,0 +1,191 @@
+<template>
+  <div>
+    <header_local />
+    <div class="main">
+      <el-row class="head">
+        <el-col :span="12"> 图片审核 </el-col>
+        <el-col style="text-align: right" :span="12">
+          <el-input
+            v-model="page_data.title"
+            placeholder="请输入文件名"
+            style="width: 250px; margin-right: 5px"
+          >
+            <template #append>
+              <el-button type="primary" @click="getList"> 检索 </el-button>
+            </template>
+          </el-input>
+          <el-button
+            type="primary"
+            :icon="UploadFilled"
+            @click="dialogVisible = true"
+          >
+            上传审核内容
+          </el-button>
+        </el-col>
+      </el-row>
+
+      <el-table :data="tableData">
+        <el-table-column align="center" type="index" width="50" />
+        <el-table-column align="center" prop="name" label="封面">
+          <template #default="scope">
+            <el-image
+              style="width: 40px; height: 40px; border-radius: 5px"
+              :src="scope.row.url"
+              fit="cover"
+            />
+          </template>
+        </el-table-column>
+        <el-table-column align="center" prop="title" label="标题">
+        </el-table-column>
+        <el-table-column
+          align="center"
+          prop="createTime"
+          label="上传时间"
+        ></el-table-column>
+        <el-table-column align="center" prop="statusCode" label="审核结果">
+          <template #default="scope">
+            <el-tag :type="scope.row.statusCode">{{
+              scope.row.statusDesc
+            }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" prop="address" label="操作">
+          <template #default="scope">
+            <el-button
+              size="small"
+              v-if="scope.row.statusCode !== 'info'"
+              @click="detail(scope.row)"
+            >
+              查看详情
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <br />
+      <el-pagination
+        background
+        layout="prev, pager, next"
+        :total="page_data.total"
+      />
+
+      <el-dialog v-model="dialogVisible" title="选择文件">
+        <div>文件名称</div>
+        <br />
+        <el-input v-model="input" placeholder="请输入文件名称" clearable />
+        <br />
+        <br />
+        <el-upload
+          v-model:file-list="fileList"
+          class="upload-demo"
+          :on-error="upError"
+          :headers="{ Authorization }"
+          :on-progress="upProgress"
+          :before-upload="upload"
+          :action="config.base.videoProcessing + '/review/img/upload'"
+        >
+          <el-button type="primary" :icon="UploadFilled"> 选择文件 </el-button>
+        </el-upload>
+        <template #footer>
+          <div class="dialog-footer">
+            <el-button type="primary" @click="submit"> 提交 </el-button>
+          </div>
+        </template>
+      </el-dialog>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import header_local from '../components/header.vue';
+import { UploadFilled } from '@element-plus/icons-vue';
+import { ElMessage } from 'element-plus';
+import { storeImg, historyImg } from '@/api/processing';
+import { ref } from 'vue';
+import config from '../../../config/index';
+import { useRouter } from 'vue-router';
+const route = useRouter();
+
+const Authorization = localStorage.getItem('token') || '';
+
+const dialogVisible = ref(false);
+const fileList = ref([]);
+const input = ref('');
+const page_data = ref({
+  page: 1,
+  pageSize: 10,
+  title: '',
+  total: 0
+});
+const tableData = ref([]);
+getList();
+
+function upProgress(par) {
+  // 上传进度
+  console.log(par);
+}
+
+function upError() {
+  ElMessage.error('请稍后重新选择文件!');
+}
+
+function upload(file) {
+  if (/^image\//.test(file.type)) return true;
+  ElMessage.error('请选择图片文件');
+  return false;
+}
+
+function submit() {
+  if (!fileList.value[0]) return ElMessage.error('请选择图片文件');
+  storeImg({
+    data: {
+      title: input.value || '',
+      url: fileList.value[0].response.data.url
+    }
+  })
+    .then(r => {
+      dialogVisible.value = false;
+      input.value = '';
+      getList();
+    })
+    .catch(e => {
+      dialogVisible.value = false;
+      input.value = '';
+    });
+}
+
+function getList() {
+  historyImg({
+    data: page_data.value
+  }).then(r => {
+    tableData.value = (r.records || []).map(v => {
+      return {
+        ...v,
+        createTime: v.createTime.replace('T', ' ')
+      };
+    });
+    page_data.value.total = r.total;
+  });
+}
+
+function detail(row) {
+  sessionStorage.setItem('Processing_detail', JSON.stringify(row));
+  route.push({
+    name: 'imageProcessing_detail'
+  });
+}
+</script>
+
+<style>
+.main {
+  padding: 1em;
+  font-size: 14px;
+  font-family: PingFangSC, PingFang SC;
+}
+
+.main .head {
+  background: #f9fafc;
+  height: 42px;
+  line-height: 42px;
+  padding: 0 22px;
+}
+</style>

+ 160 - 0
src/view/allMedia/videoProcessing/detail.vue

@@ -0,0 +1,160 @@
+<template>
+  <div>
+    <header_local />
+    <div class="main">
+      <el-row class="head">
+        <el-col :span="12"> 图片审核 </el-col>
+      </el-row>
+      <br />
+      <el-row class="body">
+        <el-col
+          ref="left"
+          :span="16"
+          style="border-right: 2px solid #e4e4e4; padding: 0 26px"
+        >
+          <div v-text="detail.title" style="margin-bottom: 1em"></div>
+          <video
+            autoplay
+            controls
+            ref="video"
+            style="margin-bottom: 3em; width: 100%"
+            @loadedmetadata="loadedmetadata"
+            :src="detail.url"
+          ></video>
+          <el-button
+            :type="status[detail.reviewResult.label]"
+            style="margin: 0 auto; display: block"
+            v-text="detail.reviewResult.labelDesc"
+          ></el-button>
+          <div style="text-align: center; height: 3em; line-height: 3em">
+            该视频共有{{ detail_data.total }}处违规
+          </div>
+        </el-col>
+        <el-col :span="8" style="padding: 0 17px">
+          <div style="height: 3em; line-height: 3em">审核结果</div>
+          <el-tabs
+            v-if="detail && detail.reviewResult && detail.reviewResult.results"
+            v-model="activeName"
+          >
+            <el-tab-pane
+              :label="item.typeDesc"
+              :name="item.type"
+              v-for="item in detail.reviewResult.results"
+              :key="item.type"
+            >
+              <div
+                :class="{ err_item: true, err_act: select_item === index }"
+                v-for="(p, index) in item.items"
+                :key="index"
+                @click="() => play(p, index)"
+              >
+                <div
+                  class="err_item_label"
+                  style="height: 2em; line-height: 2em"
+                >
+                  {{
+                    `${index + 1}、 ${changeTime(
+                      p.startTimeInSeconds
+                    )}-${changeTime(p.endTimeInSeconds)},类型:${
+                      p.labelDesc
+                    },错误内容:${p.subTypeDesc}`
+                  }}
+                </div>
+                <img
+                  :src="p.evidence.thumbnail"
+                  style="width: 100%; max-width: 373px; border-radius: 5px;display: block;margin: 0 auto;"
+                />
+              </div>
+            </el-tab-pane>
+          </el-tabs>
+        </el-col>
+      </el-row>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import header_local from '../components/header.vue';
+// import { storeImg, historyImg } from '@/api/processing';
+import { ref, onMounted } from 'vue';
+// import config from '../../../config/index';
+const status = {
+  REJECT: 'danger',
+  REVIEW: 'warning',
+  NORMAL: 'success'
+};
+const left = ref(null);
+const video = ref(null);
+const detail = JSON.parse(
+  sessionStorage.getItem('Processing_detail') || '{reviewResult: {}}'
+);
+const activeName = ref(
+  detail && detail.reviewResult && detail.reviewResult.results
+    ? detail.reviewResult.results[0].type
+    : ''
+);
+const select_item = ref(-1);
+
+const detail_data = ref({
+  total: 0
+});
+detail?.reviewResult?.results?.map(v => {
+  detail_data.value.total += v.items ? v.items.length : 0;
+});
+
+console.log(detail);
+
+function loadedmetadata(p) {
+  console.log(p.srcElement);
+}
+
+function changeTime(T) {
+  let H = (T - (T % 3600)) / 3600;
+  let M = (T - (T % 60)) / 60;
+  let S = T % 60;
+  H < 10 ? (H = '0' + H) : H;
+  M < 10 ? (M = '0' + M) : M;
+  S < 10 ? (S = '0' + S) : S;
+  return `${H}:${M}:${S}`;
+}
+
+function play(p, i) {
+  select_item.value = i;
+  video.value.currentTime = p.startTimeInSeconds;
+  video.value.play();
+}
+
+// const Authorization = localStorage.getItem('token') || '';
+</script>
+
+<style scoped>
+.main {
+  padding: 1em;
+  font-size: 14px;
+  font-family: PingFangSC, PingFang SC;
+  height: calc(100% - 42px);
+}
+
+.main .head {
+  background: #f9fafc;
+  height: 42px;
+  line-height: 42px;
+  padding: 0 22px;
+}
+
+.main .body {
+  height: calc(100% - 42px - 1em);
+}
+.err_item_label {
+  overflow-wrap: break-word;
+}
+
+.err_item {
+  padding: 0 5px;
+}
+
+.err_item:hover,
+.err_act {
+  background-color: #e8effc;
+}
+</style>

+ 190 - 0
src/view/allMedia/videoProcessing/index.vue

@@ -0,0 +1,190 @@
+<template>
+  <div>
+    <header_local />
+    <div class="main">
+      <el-row class="head">
+        <el-col :span="12"> 视频审核 </el-col>
+        <el-col style="text-align: right" :span="12">
+          <el-input
+            v-model="page_data.title"
+            placeholder="请输入文件名"
+            style="width: 250px; margin-right: 5px"
+          >
+            <template #append>
+              <el-button type="primary" @click="getList"> 检索 </el-button>
+            </template>
+          </el-input>
+          <el-button
+            type="primary"
+            :icon="UploadFilled"
+            @click="dialogVisible = true"
+          >
+            上传审核内容
+          </el-button>
+        </el-col>
+      </el-row>
+
+      <el-table :data="tableData">
+        <el-table-column align="center" type="index" width="50" />
+        <el-table-column align="center" prop="name" label="封面">
+          <template #default="scope">
+            <el-image
+              style="width: 40px; height: 40px; border-radius: 5px"
+              :src="scope.row.cover"
+              fit="cover"
+            />
+          </template>
+        </el-table-column>
+        <el-table-column align="center" prop="title" label="标题">
+        </el-table-column>
+        <el-table-column
+          align="center"
+          prop="createTime"
+          label="上传时间"
+        ></el-table-column>
+        <el-table-column align="center" prop="statusCode" label="审核结果">
+          <template #default="scope">
+            <el-tag :type="scope.row.statusCode">{{
+              scope.row.statusDesc
+            }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" prop="address" label="操作">
+          <template #default="scope">
+            <el-button size="small" v-if="scope.row.statusCode !== 'info'" @click="detail(scope.row)"> 查看详情 </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <br />
+      <el-pagination
+        background
+        layout="prev, pager, next"
+        :total="page_data.total"
+      />
+
+      <el-dialog v-model="dialogVisible" title="选择文件">
+        <div>文件名称</div>
+        <br />
+        <el-input v-model="input" placeholder="请输入文件名称" clearable />
+        <br />
+        <br />
+        <el-upload
+          v-model:file-list="fileList"
+          class="upload-demo"
+          :on-error="upError"
+          :headers="{ Authorization }"
+          :on-progress="upProgress"
+          :before-upload="upload"
+          :action="config.base.videoProcessing + '/review/img/upload'"
+        >
+          <el-button type="primary" :icon="UploadFilled"> 选择文件 </el-button>
+        </el-upload>
+        <template #footer>
+          <div class="dialog-footer">
+            <el-button type="primary" @click="submit"> 提交 </el-button>
+          </div>
+        </template>
+      </el-dialog>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import header_local from '../components/header.vue';
+import { UploadFilled } from '@element-plus/icons-vue';
+import { ElMessage } from 'element-plus';
+import { storeVideo, historyVideo } from '@/api/processing';
+import { ref } from 'vue';
+import config from '../../../config/index';
+import { useRouter } from 'vue-router';
+const route = useRouter();
+
+const Authorization = localStorage.getItem('token') || '';
+
+const dialogVisible = ref(false);
+const fileList = ref([]);
+const input = ref('');
+const page_data = ref({
+  page: 1,
+  pageSize: 10,
+  title: '',
+  total: 0
+});
+const tableData = ref([]);
+getList();
+
+function upProgress(par) {
+  // 上传进度
+  console.log(par);
+}
+
+function upError() {
+  ElMessage.error('请稍后重新选择文件!');
+}
+
+function upload(file) {
+  if (/^video\//.test(file.type)) return true;
+  ElMessage.error('请选择视频文件');
+  return false;
+}
+
+function submit() {
+  if (!fileList.value[0]) return ElMessage.error('请选择视频文件');
+  storeVideo({
+    data: {
+      title: input.value || '',
+      url: fileList.value[0].response.data.url
+    }
+  })
+    .then(r => {
+      dialogVisible.value = false;
+      input.value = '';
+      getList();
+    })
+    .catch(e => {
+      dialogVisible.value = false;
+      input.value = '';
+    });
+}
+
+function getList() {
+  historyVideo({
+    data: page_data.value
+  })
+    .then(r => {
+      tableData.value = (r.records || []).map(v => {
+        return {
+          ...v,
+          createTime: v.createTime.replace('T', ' ')
+        };
+      });
+      page_data.value.total = r.total;
+    });
+}
+
+
+function detail(row) {
+  sessionStorage.setItem('Processing_detail', JSON.stringify(row));
+  route.push({
+    path: '/main_home/videoProcessing_detail',
+    params: {
+      item: JSON.stringify(row)
+    }
+  });
+}
+</script>
+
+<style>
+.main {
+  padding: 1em;
+  font-size: 14px;
+  font-family: PingFangSC, PingFang SC;
+}
+
+.main .head {
+  background: #f9fafc;
+  height: 42px;
+  line-height: 42px;
+  padding: 0 22px;
+}
+</style>

Деякі файли не було показано, через те що забагато файлів було змінено