yanghao před 2 dny
rodič
revize
857f39b09f

+ 1 - 0
components.d.ts

@@ -24,6 +24,7 @@ declare module 'vue' {
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
     ElIcon: typeof import('element-plus/es')['ElIcon']
     ElInput: typeof import('element-plus/es')['ElInput']
+    ElPagination: typeof import('element-plus/es')['ElPagination']
     ElPopover: typeof import('element-plus/es')['ElPopover']
     ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
     ElSkeletonItem: typeof import('element-plus/es')['ElSkeletonItem']

+ 18 - 4
src/api/user.ts

@@ -93,16 +93,30 @@ export const getFlows = async (params) => {
 };
 
 // oa待办任务
-export const getOATasks = async (id) => {
+export const getOATasks = async (params) => {
   return await request.get({
-    url: "/admin-api/portal/todo/oa?workcode=" + id,
+    url:
+      "/admin-api/portal/todo/oa?workcode=" +
+      params.id +
+      "&pageNo=" +
+      params.pageNum +
+      "&pageSize=" +
+      params.pageSize,
   });
 };
 
 // crm待办任务
-export const getCRMTasks = async (id) => {
+export const getCRMTasks = async (params) => {
   return await request.get({
-    url: "/admin-api/portal/todo/crm?workcode=" + id,
+    url:
+      "/admin-api/portal/todo/crm?workcode=" +
+      params.id +
+      "&type=" +
+      params.type +
+      "&pageNo=" +
+      params.pageNum +
+      "&pageSize=" +
+      params.pageSize,
   });
 };
 

+ 7 - 0
src/main.ts

@@ -6,6 +6,9 @@ import { setupStore } from "@/stores";
 import "./assets/style/main.css";
 import "element-plus/dist/index.css";
 
+import ElementPlus from "element-plus";
+import zhCn from "element-plus/es/locale/lang/zh-cn";
+
 import App from "./App.vue";
 import router from "./router";
 
@@ -13,6 +16,10 @@ const app = createApp(App);
 
 setupStore(app);
 
+app.use(ElementPlus, {
+  locale: zhCn,
+});
+
 app.use(router);
 
 app.mount("#app");

+ 18 - 0
src/router/index.ts

@@ -48,6 +48,15 @@ const routes: RouteRecordRaw[] = [
     },
   },
 
+  {
+    path: "/oa-done-list",
+    name: "OADoneList",
+    component: () => import("@/views/flow/oaDoneList.vue"),
+    meta: {
+      title: "DeepOil 智慧经营平台 | 已办列表",
+    },
+  },
+
   {
     path: "/crm-todo-list",
     name: "CRMTodoList",
@@ -56,6 +65,15 @@ const routes: RouteRecordRaw[] = [
       title: "DeepOil 智慧经营平台 | CRM待办列表",
     },
   },
+
+  {
+    path: "/crm-done-list",
+    name: "CRMDoneList",
+    component: () => import("@/views/flow/crmDoneList.vue"),
+    meta: {
+      title: "DeepOil 智慧经营平台 | CRM已办列表",
+    },
+  },
 ];
 
 const router = createRouter({

+ 350 - 0
src/views/flow/crmDoneList.vue

@@ -0,0 +1,350 @@
+<template>
+  <div class="todo-list">
+    <Header />
+
+    <div class="content mt-15">
+      <div class="flex gap-5 items-center mb-4 bg-[#f9f9f9] py-2">
+        <p class="flex items-center">
+          <Icon
+            icon="mynaui:arrow-up-down"
+            class="icon pr-1 h-6 w-6"
+            color="#014099"
+          />CRM待办任务列表
+        </p>
+
+        <el-button
+          type="primary"
+          round
+          size="default"
+          color="#02409b"
+          @click="router.back()"
+          ><Icon
+            icon="mynaui:corner-up-left"
+            class="icon pr-1"
+            width="20"
+            height="20"
+          />返回</el-button
+        >
+      </div>
+      <div class="h-[80vh]">
+        <el-table
+          :data="oaTasks"
+          style="width: 100%"
+          :empty-text="loading ? '加载中...' : '暂无数据'"
+          :header-cell-style="{
+            backgroundColor: '#e9f7ff',
+            color: 'black',
+            fontWeight: '400',
+          }"
+          :cell-style="{
+            color: 'black',
+          }"
+        >
+          <el-table-column
+            type="index"
+            label="序号"
+            width="80"
+            fixed="left"
+            align="center"
+          />
+          <el-table-column
+            prop="instTitle"
+            label="流程标题"
+            min-width="220"
+            fixed="left"
+            align="center"
+          />
+          <el-table-column
+            prop="entityTypeId"
+            label="业务类型"
+            width="140"
+            align="center"
+          />
+          <el-table-column
+            prop="priority"
+            label="优先级别"
+            width="100"
+            align="center"
+          >
+            <template #default="scope">
+              <el-tag v-if="scope.row.priority == 50" type="warning" size="mini"
+                >一般</el-tag
+              >
+              <el-tag
+                v-else-if="scope.row.priority === 90"
+                type="danger"
+                size="mini"
+              >
+                紧急
+              </el-tag>
+
+              <span v-else></span>
+            </template>
+          </el-table-column>
+
+          <el-table-column
+            prop="createAt"
+            label="任务提交时间"
+            width="180"
+            align="center"
+          >
+            <template #default="scope">
+              {{ timestampToDateTime(scope.row.submitAt) }}
+            </template>
+          </el-table-column>
+
+          <el-table-column
+            prop="createAt"
+            label="任务创建时间"
+            width="180"
+            align="center"
+          >
+            <template #default="scope">
+              {{ timestampToDateTime(scope.row.createdAt) }}
+            </template>
+          </el-table-column>
+
+          <el-table-column
+            prop="endAt"
+            label="任务完成时间"
+            width="180"
+            align="center"
+          >
+            <template #default="scope">
+              {{ timestampToDateTime(scope.row.endAt) }}
+            </template>
+          </el-table-column>
+
+          <el-table-column
+            prop="userName"
+            label="任务提交人"
+            width="120"
+            align="center"
+          />
+
+          <el-table-column
+            prop="status"
+            label="任务状态"
+            width="100"
+            align="center"
+          >
+            <template #default="scope">
+              <el-tag v-if="scope.row.status === 1" type="info" size="mini"
+                >待提交</el-tag
+              >
+              <el-tag
+                v-else-if="scope.row.status === 2"
+                type="info"
+                size="mini"
+              >
+                待重新提交
+              </el-tag>
+
+              <el-tag
+                v-else-if="scope.row.status === 3"
+                type="info"
+                size="mini"
+              >
+                待提交到退回节点
+              </el-tag>
+
+              <el-tag
+                v-else-if="scope.row.status === 4"
+                type="primary"
+                size="mini"
+              >
+                已提交
+              </el-tag>
+
+              <el-tag
+                v-else-if="scope.row.status === 5"
+                type="info"
+                size="mini"
+              >
+                待办理
+              </el-tag>
+
+              <el-tag
+                v-else-if="scope.row.status === 6"
+                type="success"
+                size="mini"
+              >
+                已同意
+              </el-tag>
+
+              <el-tag
+                v-else-if="scope.row.status === 7"
+                type="danger"
+                size="mini"
+              >
+                已拒绝
+              </el-tag>
+
+              <el-tag
+                v-else-if="scope.row.status === 8"
+                type="danger"
+                size="mini"
+              >
+                已转办
+              </el-tag>
+
+              <el-tag
+                v-else-if="scope.row.status === 9"
+                type="primary"
+                size="mini"
+              >
+                已抄送
+              </el-tag>
+
+              <el-tag
+                v-else-if="scope.row.status === 10"
+                type="warning"
+                size="mini"
+              >
+                加签挂起
+              </el-tag>
+
+              <el-tag
+                v-else-if="scope.row.status === 11"
+                type="warning"
+                size="mini"
+              >
+                任务挂起
+              </el-tag>
+
+              <el-tag
+                v-else-if="scope.row.status === 12"
+                type="warning"
+                size="mini"
+              >
+                已收回
+              </el-tag>
+
+              <el-tag
+                v-else-if="scope.row.status === 13"
+                type="warning"
+                size="mini"
+              >
+                已移交
+              </el-tag>
+
+              <el-tag
+                v-else-if="scope.row.status === 14"
+                type="warning"
+                size="mini"
+              >
+                已委托
+              </el-tag>
+
+              <el-tag
+                v-else-if="scope.row.status === 99"
+                type="success"
+                size="mini"
+              >
+                无需办理
+              </el-tag>
+              <span v-else></span>
+            </template>
+          </el-table-column>
+
+          <el-table-column
+            label="操作"
+            width="120"
+            fixed="right"
+            align="center"
+          >
+            <template #default="scope">
+              <span
+                class="text-[#02409b] cursor-pointer"
+                @click="goBackPage(scope.row)"
+                >处理</span
+              >
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </div>
+
+    <Footer />
+  </div>
+</template>
+
+<script setup>
+import Header from "@components/home/header.vue";
+import { ref, onMounted } from "vue";
+import { getCRMTasks, ssoLogin } from "@/api/user";
+import { useUserStore } from "@/stores/useUserStore";
+import { Icon } from "@iconify/vue";
+import router from "@/router";
+const userStore = useUserStore();
+
+const oaTasks = ref([]);
+const loading = ref(false);
+
+const goBackPage = async (row) => {
+  // const res = await ssoLogin({
+  //   username: userStore.getUser.username,
+  // });
+  // if (res) {
+  //   const newTab = window.open("", "_blank");
+  //   newTab.location.href =
+  //     "https://yfoa.keruioil.com/wui/index.html" +
+  //     "?ssoToken=" +
+  //     res +
+  //     "#/main";
+  //   setTimeout(function () {
+  //     newTab.location.href = `https://yfoa.keruioil.com/spa/workflow/static4form/index.html?_rdm=1776063595284#/main/workflow/req?requestid=${row.requestId}`;
+  //   }, 50);
+  // }
+};
+
+function timestampToDateTime(timestamp) {
+  // 兼容 10位(秒) / 13位(毫秒)
+  const len = String(timestamp).length;
+  const date = new Date(Number(timestamp) * (len === 10 ? 1000 : 1));
+
+  // 年
+  const year = date.getFullYear();
+  // 月(0~11 → +1)
+  const month = String(date.getMonth() + 1).padStart(2, "0");
+  // 日
+  const day = String(date.getDate()).padStart(2, "0");
+  // 时
+  const hours = String(date.getHours()).padStart(2, "0");
+  // 分
+  const minutes = String(date.getMinutes()).padStart(2, "0");
+  // 秒
+  const seconds = String(date.getSeconds()).padStart(2, "0");
+
+  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+}
+onMounted(async () => {
+  if (userStore.getUser.username) {
+    loading.value = true;
+    try {
+      const res = await getCRMTasks({
+        id: userStore.getUser.username,
+        type: "approved",
+        pageNum: 1,
+        pageSize: 10,
+      });
+      oaTasks.value = res.todoList;
+    } finally {
+      loading.value = false;
+    }
+  }
+});
+</script>
+
+<style scoped>
+.todo-list {
+  display: flex;
+  flex-direction: column;
+  min-height: 100%;
+}
+
+.content {
+  padding: 16px 20px;
+  margin-top: 100px;
+}
+</style>

+ 7 - 2
src/views/flow/crmTodoList.vue

@@ -116,7 +116,7 @@
           </el-table-column>
 
           <el-table-column
-            prop="submitterId"
+            prop="userName"
             label="任务提交人"
             width="120"
             align="center"
@@ -322,7 +322,12 @@ onMounted(async () => {
   if (userStore.getUser.username) {
     loading.value = true;
     try {
-      const res = await getCRMTasks(userStore.getUser.username);
+      const res = await getCRMTasks({
+        id: userStore.getUser.username,
+        type: "approved",
+        pageNum: 1,
+        pageSize: 10,
+      });
       oaTasks.value = res.todoList;
     } finally {
       loading.value = false;

+ 71 - 18
src/views/flow/index.vue

@@ -181,6 +181,7 @@ import { useUserStore } from "@/stores/useUserStore";
 import { getAccessToken } from "@/utils/auth";
 import * as echarts from "echarts";
 import { useRouter } from "vue-router";
+import { pa } from "element-plus/es/locales.mjs";
 const router = useRouter();
 
 const userStore = useUserStore();
@@ -418,7 +419,7 @@ const go = async (item) => {
 
         setTimeout(function () {
           newTab.location.href = item.flowUrl;
-        }, 50);
+        }, 100);
       }
     }
 
@@ -432,7 +433,7 @@ const go = async (item) => {
 
       setTimeout(function () {
         newTab.location.href = item.flowUrl;
-      }, 50);
+      }, 100);
     }
   } else {
     router.push({ path: "/login" });
@@ -449,12 +450,26 @@ const handleDetailClick = (task, categoryTitle) => {
     });
   }
 
+  if (task.name === "OA" && categoryTitle === "已办事项") {
+    router.push({
+      path: "/oa-done-list",
+      query: { type: task.name.toLowerCase() },
+    });
+  }
+
   if (task.name === "CRM" && categoryTitle === "我的待办") {
     router.push({
       path: "/crm-todo-list",
       query: { type: task.name.toLowerCase() },
     });
   }
+
+  if (task.name === "CRM" && categoryTitle === "已办事项") {
+    router.push({
+      path: "/crm-done-list",
+      query: { type: task.name.toLowerCase() },
+    });
+  }
 };
 
 let oaTasks = ref([]);
@@ -490,18 +505,6 @@ const doneCount = ref([
   { name: "CRM", value: 0 },
 ]);
 
-// 发起流程
-const startCount = ref([
-  { name: "OA", value: 0 },
-  { name: "CRM", value: 0 },
-]);
-
-// 草稿箱
-const drafts = ref([
-  { name: "OA", value: 0 },
-  { name: "CRM", value: 0 },
-]);
-
 onMounted(async () => {
   getAll();
   // 等待 DOM 与样式生效,避免移动端首屏尺寸为 0
@@ -522,10 +525,19 @@ onMounted(async () => {
 
   if (userStore.getUser.username) {
     try {
-      const res = await getOATasks(userStore.getUser.username);
+      const res = await getOATasks({
+        id: userStore.getUser.username,
+        pageNum: 1,
+        pageSize: 99,
+      });
       oaTasks.value = res;
 
-      const crmRes = await getCRMTasks(userStore.getUser.username);
+      const crmRes = await getCRMTasks({
+        id: userStore.getUser.username,
+        type: "pending",
+        pageNum: 1,
+        pageSize: 10,
+      });
       crmTasks.value = crmRes;
 
       stats.value[0].number =
@@ -535,16 +547,42 @@ onMounted(async () => {
         { name: "OA", value: oaTasks.value.todoCount ?? 0 },
         { name: "CRM", value: crmTasks.value.todoCount ?? 0 },
       ];
+
+      // *****************已办事项统计*************************
+
+      const crmDoneRes = await getCRMTasks({
+        id: userStore.getUser.username,
+        type: "approved",
+        pageNum: 1,
+        pageSize: 10,
+      });
+
+      stats.value[1].number =
+        Number(oaTasks.value.doneCount) + Number(crmDoneRes.todoCount);
+
+      doneCount.value = [
+        { name: "OA", value: oaTasks.value.doneCount ?? 0 },
+        { name: "CRM", value: crmDoneRes.todoCount ?? 0 },
+      ];
     } finally {
       statsLoading.value = false;
     }
 
     setInterval(
       async () => {
-        const res = await getOATasks(userStore.getUser.username);
+        const res = await getOATasks({
+          id: userStore.getUser.username,
+          pageNum: 1,
+          pageSize: 10,
+        });
         oaTasks.value = res;
 
-        const crmRes = await getCRMTasks(userStore.getUser.username);
+        const crmRes = await getCRMTasks({
+          id: userStore.getUser.username,
+          type: "pending",
+          pageNum: 1,
+          pageSize: 10,
+        });
         crmTasks.value = crmRes;
 
         stats.value[0].number =
@@ -554,6 +592,21 @@ onMounted(async () => {
           { name: "OA", value: oaTasks.value.todoCount ?? 0 },
           { name: "CRM", value: crmTasks.value.todoCount ?? 0 },
         ];
+
+        const crmDoneRes = await getCRMTasks({
+          id: userStore.getUser.username,
+          type: "approved",
+          pageNum: 1,
+          pageSize: 10,
+        });
+
+        stats.value[1].number =
+          Number(oaTasks.value.doneCount) + Number(crmDoneRes.todoCount);
+
+        doneCount.value = [
+          { name: "OA", value: oaTasks.value.doneCount ?? 0 },
+          { name: "CRM", value: crmDoneRes.todoCount ?? 0 },
+        ];
       },
       5 * 60 * 1000,
     ); // 每5分钟刷新一次

+ 263 - 0
src/views/flow/oaDoneList.vue

@@ -0,0 +1,263 @@
+<template>
+  <div class="todo-list">
+    <Header />
+
+    <div class="content mt-15">
+      <div class="flex gap-5 items-center mb-4 bg-[#f9f9f9] py-2">
+        <p class="flex items-center">
+          <Icon
+            icon="mynaui:arrow-up-down"
+            class="icon pr-1 h-6 w-6"
+            color="#014099"
+          />&#24050;&#21150;&#20219;&#21153;&#21015;&#34920;
+        </p>
+
+        <el-button
+          type="primary"
+          round
+          size="default"
+          color="#02409b"
+          @click="router.back()"
+          ><Icon
+            icon="mynaui:corner-up-left"
+            class="icon pr-1"
+            width="20"
+            height="20"
+          />&#36820;&#22238;</el-button
+        >
+      </div>
+      <div class="h-[80vh]">
+        <el-table
+          :data="oaTasks"
+          style="width: 100%"
+          :empty-text="
+            loading
+              ? '&#21152;&#36733;&#20013;...'
+              : '&#26242;&#26080;&#25968;&#25454;'
+          "
+          :header-cell-style="{
+            backgroundColor: '#e9f7ff',
+            color: 'black',
+            fontWeight: '400',
+          }"
+          :cell-style="{
+            color: 'black',
+          }"
+        >
+          <el-table-column
+            type="index"
+            label="&#24207;&#21495;"
+            width="80"
+            fixed="left"
+            align="center"
+            :index="getIndex"
+          />
+          <el-table-column
+            prop="requestName"
+            label="&#35831;&#27714;&#26631;&#39064;"
+            min-width="220"
+            fixed="left"
+            align="center"
+          />
+          <el-table-column
+            prop="workflowBaseInfo.workflowTypeName"
+            label="&#27969;&#31243;&#31867;&#22411;"
+            width="140"
+            align="center"
+          />
+          <el-table-column
+            prop="requestLevel"
+            label="&#32039;&#24613;&#31243;&#24230;"
+            width="100"
+            align="center"
+          />
+          <el-table-column
+            prop="status"
+            label="&#36335;&#24452;&#29366;&#24577;"
+            width="100"
+            align="center"
+          />
+          <el-table-column
+            prop="sysName"
+            label="&#31995;&#32479;&#21517;&#31216;"
+            width="140"
+            align="center"
+          />
+          <el-table-column
+            prop="createTime"
+            label="&#21019;&#24314;&#26102;&#38388;"
+            width="180"
+            align="center"
+          />
+
+          <el-table-column
+            prop="creatorName"
+            label="&#21019;&#24314;&#20154;&#21517;&#31216;"
+            width="120"
+            align="center"
+          />
+
+          <el-table-column
+            prop="currentNodeName"
+            label="&#24403;&#21069;&#33410;&#28857;&#21517;&#31216;"
+            width="140"
+            align="center"
+          />
+          <el-table-column
+            prop="lastOperateTime"
+            label="&#26368;&#21518;&#25805;&#20316;&#26102;&#38388;"
+            width="180"
+            align="center"
+          />
+
+          <el-table-column
+            prop="lastOperatorName"
+            label="&#26368;&#21518;&#25805;&#20316;&#20154;&#21517;&#31216;"
+            width="140"
+            align="center"
+          />
+          <el-table-column
+            prop="receiveTime"
+            label="&#25509;&#25910;&#26102;&#38388;"
+            width="180"
+            align="center"
+          />
+
+          <el-table-column
+            prop="workflowBaseInfo.workflowName"
+            label="&#27969;&#31243;&#21517;&#31216;"
+            min-width="200"
+            align="center"
+          />
+
+          <el-table-column
+            label="&#25805;&#20316;"
+            width="120"
+            fixed="right"
+            align="center"
+          >
+            <template #default="scope">
+              <span
+                class="text-[#02409b] cursor-pointer"
+                @click="goBackPage(scope.row)"
+                >&#22788;&#29702;</span
+              >
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <div class="pagination-wrap">
+          <el-pagination
+            v-model:current-page="pagination.pageNum"
+            v-model:page-size="pagination.pageSize"
+            :total="pagination.total"
+            :page-sizes="[10, 20, 50, 100]"
+            layout="total, sizes, prev, pager, next"
+            background
+            @current-change="handleCurrentChange"
+            @size-change="handleSizeChange"
+          />
+        </div>
+      </div>
+    </div>
+
+    <Footer />
+  </div>
+</template>
+
+<script setup>
+import Header from "@components/home/header.vue";
+import { ref, onMounted } from "vue";
+import { getOATasks, ssoLogin } from "@/api/user";
+import { useUserStore } from "@/stores/useUserStore";
+import { Icon } from "@iconify/vue";
+
+import router from "@/router";
+const userStore = useUserStore();
+
+const oaTasks = ref([]);
+const loading = ref(false);
+const pagination = ref({
+  pageNum: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const getIndex = (index) => {
+  return (pagination.value.pageNum - 1) * pagination.value.pageSize + index + 1;
+};
+
+const getDoneList = async () => {
+  if (!userStore.getUser.username) return;
+
+  loading.value = true;
+  try {
+    const res = await getOATasks({
+      id: userStore.getUser.username,
+      pageNum: pagination.value.pageNum,
+      pageSize: pagination.value.pageSize,
+    });
+
+    oaTasks.value = res.doneList || [];
+    pagination.value.total = Number(res.doneCount ?? oaTasks.value.length ?? 0);
+  } finally {
+    loading.value = false;
+  }
+};
+
+const handleCurrentChange = (page) => {
+  pagination.value.pageNum = page;
+  getDoneList();
+};
+
+const handleSizeChange = (size) => {
+  pagination.value.pageSize = size;
+  pagination.value.pageNum = 1;
+  getDoneList();
+};
+
+const goBackPage = async (row) => {
+  const res = await ssoLogin({
+    username: userStore.getUser.username,
+  });
+
+  if (res) {
+    const newTab = window.open("", "_blank");
+
+    newTab.location.href =
+      "https://yfoa.keruioil.com/wui/index.html" +
+      "?ssoToken=" +
+      res +
+      "#/main";
+
+    setTimeout(function () {
+      newTab.location.href = `https://yfoa.keruioil.com/spa/workflow/static4form/index.html?_rdm=1776063595284#/main/workflow/req?requestid=${row.requestId}`;
+    }, 100);
+  }
+};
+
+onMounted(async () => {
+  if (userStore.getUser.username) {
+    getDoneList();
+  }
+});
+</script>
+
+<style scoped>
+.todo-list {
+  display: flex;
+  flex-direction: column;
+  min-height: 100%;
+}
+
+.content {
+  padding: 16px 20px;
+  margin-top: 100px;
+}
+
+.pagination-wrap {
+  display: flex;
+  justify-content: flex-end;
+  margin-top: 16px;
+}
+</style>

+ 6 - 2
src/views/flow/todoList.vue

@@ -175,14 +175,18 @@ const goBackPage = async (row) => {
 
     setTimeout(function () {
       newTab.location.href = `https://yfoa.keruioil.com/spa/workflow/static4form/index.html?_rdm=1776063595284#/main/workflow/req?requestid=${row.requestId}`;
-    }, 50);
+    }, 100);
   }
 };
 onMounted(async () => {
   if (userStore.getUser.username) {
     loading.value = true;
     try {
-      const res = await getOATasks(userStore.getUser.username);
+      const res = await getOATasks({
+        id: userStore.getUser.username,
+        pageNum: 1,
+        pageSize: 10,
+      });
       oaTasks.value = res.todoList;
     } finally {
       loading.value = false;