From a9d97b150701e634bdb751eab277696abd136cca Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 16 六月 2026 14:39:47 +0800
Subject: [PATCH] 君歌app 1.依照web端功能修改
---
src/pages/oa/_components/ApprovalInstanceListPage.vue | 353 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 353 insertions(+), 0 deletions(-)
diff --git a/src/pages/oa/_components/ApprovalInstanceListPage.vue b/src/pages/oa/_components/ApprovalInstanceListPage.vue
new file mode 100644
index 0000000..02b1f37
--- /dev/null
+++ b/src/pages/oa/_components/ApprovalInstanceListPage.vue
@@ -0,0 +1,353 @@
+<!--
+ 涓氬姟瀹℃壒鐢宠鍒楄〃锛堣浆姝�/璋冨矖/浜ゆ帴/璇峰亣/鍔犵彮锛�
+-->
+<template>
+ <view class="oa-approval-page">
+ <PageHeader :title="pageTitle"
+ @back="goBack" />
+
+ <view class="oa-toolbar">
+ <view class="oa-filter-chip"
+ :class="{ active: hasActiveFilter }"
+ @click="showFilter = true">
+ <up-icon name="list"
+ size="18"
+ :color="hasActiveFilter ? '#2979ff' : '#666'" />
+ <text class="chip-label">绛涢��</text>
+ <text v-if="filterSummary"
+ class="chip-value">{{ filterSummary }}</text>
+ <text v-else
+ class="chip-placeholder">鍏ㄩ儴鏉′欢</text>
+ </view>
+ <view class="oa-icon-btn"
+ @click="handleSearch">
+ <up-icon name="search"
+ size="20"
+ color="#666" />
+ </view>
+ </view>
+
+ <ApprovalModuleSearchPopup v-model:show="showFilter"
+ :module-key="moduleKey"
+ v-model="searchForm"
+ @search="handleSearch"
+ @reset="handleReset" />
+
+ <scroll-view class="oa-list-scroll"
+ scroll-y
+ :show-scrollbar="false"
+ :style="{ height: listScrollHeight + 'px' }"
+ @scrolltolower="loadMore">
+ <view v-if="displayList.length"
+ class="oa-card-list">
+ <view v-for="item in displayList"
+ :key="item.id"
+ class="oa-card"
+ @click="openDetail(item)">
+ <view class="oa-card-head">
+ <view class="oa-card-title-wrap">
+ <text class="oa-card-title">{{ cardTitle(item) }}</text>
+ <text v-if="item.instanceNo"
+ class="oa-card-sub">{{ item.instanceNo }}</text>
+ </view>
+ <text :class="['oa-status', businessStatusClass(item.status)]">
+ {{ businessStatusText(item.status) }}
+ </text>
+ </view>
+
+ <view class="oa-card-body">
+ <view class="oa-info-grid">
+ <view v-for="(row, idx) in visibleDisplayRows(item)"
+ :key="'f-' + idx"
+ class="oa-info-row">
+ <text class="oa-info-label">{{ row.label }}</text>
+ <text class="oa-info-value">{{ row.value || "-" }}</text>
+ </view>
+ <view class="oa-info-row">
+ <text class="oa-info-label">鐢宠浜�</text>
+ <text class="oa-info-value">{{ item.applicantName || "-" }}</text>
+ </view>
+ <view class="oa-info-row">
+ <text class="oa-info-label">鐢宠鏃堕棿</text>
+ <text class="oa-info-value">{{ item.createTime || "-" }}</text>
+ </view>
+ </view>
+ </view>
+
+ <view v-if="canEditBusinessInstanceRow(item)"
+ class="oa-card-foot"
+ @click.stop>
+ <text class="oa-foot-btn btn-edit"
+ @click="goEdit(item)">淇敼</text>
+ <text class="oa-foot-btn btn-delete"
+ @click="confirmDelete(item)">鍒犻櫎</text>
+ </view>
+ </view>
+ <up-loadmore :status="pageStatus" />
+ </view>
+
+ <view v-else-if="!tableLoading"
+ class="oa-empty">
+ <up-empty mode="list"
+ :text="`鏆傛棤${pageTitle}鏁版嵁`" />
+ </view>
+ <view v-if="tableLoading && !list.length"
+ class="oa-loading">
+ <up-loading-icon mode="circle" />
+ </view>
+ </scroll-view>
+
+ <view class="fab-button"
+ @click="handleAdd">
+ <up-icon name="plus"
+ size="28"
+ color="#ffffff" />
+ </view>
+ </view>
+</template>
+
+<script setup>
+ import { computed, onMounted, reactive, ref } from "vue";
+ import { onShow } from "@dcloudio/uni-app";
+ import PageHeader from "@/components/PageHeader.vue";
+ import ApprovalModuleSearchPopup from "./ApprovalModuleSearchPopup.vue";
+ import {
+ deleteApprovalInstance,
+ listApprovalInstancePage,
+ } from "@/api/oa/approvalInstance.js";
+ import { OA_NAV } from "@/config/oaPaths.js";
+ import { fetchApprovalTemplateTypes } from "../_utils/approvalTemplateType.js";
+ import {
+ getApprovalModuleConfig,
+ getModuleListBusinessType,
+ } from "../_utils/approvalModuleRegistry.js";
+ import {
+ buildModuleListDto,
+ createModuleSearchForm,
+ filterRowsByModuleSearch,
+ filterRowsByModuleBusinessType,
+ formatDateRangeLabel,
+ getModuleSearchMeta,
+ hasActiveModuleSearch,
+ } from "../_utils/approvalModuleListSearch.js";
+ import {
+ buildInstanceListParams,
+ businessStatusClass,
+ businessStatusText,
+ canEditBusinessInstanceRow,
+ EDIT_STORAGE_KEY,
+ mapInstanceListRow,
+ stashInstanceRow,
+ unwrapInstancePage,
+ } from "../_utils/approveListUtils.js";
+
+ const props = defineProps({
+ moduleKey: { type: String, required: true },
+ });
+
+ const moduleConfig = computed(() => getApprovalModuleConfig(props.moduleKey));
+ const pageTitle = computed(() => moduleConfig.value?.label || "鐢宠");
+
+ const showFilter = ref(false);
+ const searchForm = reactive(createModuleSearchForm(props.moduleKey));
+ const list = ref([]);
+ const tableLoading = ref(false);
+ const pageStatus = ref("loadmore");
+ const businessType = ref("");
+ const typeOptions = ref([]);
+
+ const page = reactive({ current: 1, size: 10, total: 0 });
+ const listScrollHeight = ref(400);
+
+ function calcListScrollHeight() {
+ const sys = uni.getSystemInfoSync();
+ const statusBar = sys.statusBarHeight || 0;
+ const navBar = 44;
+ const toolbar = 56;
+ const fabGap = 16;
+ listScrollHeight.value = Math.max(
+ 200,
+ sys.windowHeight - statusBar - navBar - toolbar - fabGap
+ );
+ }
+
+ const displayList = computed(() => {
+ const byType = filterRowsByModuleBusinessType(
+ props.moduleKey,
+ list.value,
+ typeOptions.value
+ );
+ return filterRowsByModuleSearch(props.moduleKey, byType, searchForm);
+ });
+
+ const hasActiveFilter = computed(() => Boolean(filterSummary.value));
+
+ const filterSummary = computed(() => {
+ const parts = [];
+ const meta = getModuleSearchMeta(props.moduleKey);
+ for (const field of meta.fields || []) {
+ const val = searchForm[field.key];
+ if (field.type === "input" && (val || "").trim()) {
+ parts.push(`${field.label}:${String(val).trim()}`);
+ } else if (field.type === "daterange" && Array.isArray(val) && val[0]) {
+ parts.push(`${field.label}:${formatDateRangeLabel(val)}`);
+ } else if (field.type === "select" && val) {
+ const opt = (field.options || []).find(o => o.value === val);
+ parts.push(`${field.label}:${opt?.label || val}`);
+ } else if (field.type === "user" && val) {
+ parts.push(`${field.label}:宸查�塦);
+ }
+ }
+ return parts.join("锛�");
+ });
+
+ function cardTitle(item) {
+ return item.summary || item.title || pageTitle.value;
+ }
+
+ function visibleDisplayRows(item) {
+ const rows = item.displayRows || [];
+ return rows.slice(0, 2);
+ }
+
+ const buildListRequestParams = () => {
+ const extraDto = buildModuleListDto(props.moduleKey, searchForm);
+ return buildInstanceListParams({
+ page,
+ businessType: businessType.value,
+ extraDto,
+ searchForm,
+ });
+ };
+
+ const fetchList = async (reset = false) => {
+ if (reset) {
+ page.current = 1;
+ pageStatus.value = "loadmore";
+ list.value = [];
+ }
+ if (pageStatus.value === "loading" || pageStatus.value === "nomore") return;
+ if (!businessType.value && businessType.value !== 0) return;
+
+ pageStatus.value = "loading";
+ tableLoading.value = true;
+
+ try {
+ const res = await listApprovalInstancePage(buildListRequestParams());
+ const { records, total } = unwrapInstancePage(res);
+ const listFields = moduleConfig.value?.listFields || [];
+ let mapped = records.map(row => mapInstanceListRow(row, listFields));
+ if (hasActiveModuleSearch(props.moduleKey, searchForm)) {
+ mapped = filterRowsByModuleSearch(props.moduleKey, mapped, searchForm);
+ }
+
+ if (page.current === 1) {
+ list.value = mapped;
+ } else {
+ list.value = [...list.value, ...mapped];
+ }
+ const dropped = records.length - mapped.length;
+ page.total = hasActiveModuleSearch(props.moduleKey, searchForm)
+ ? list.value.length
+ : dropped > 0
+ ? Math.max(0, Number(total) - dropped)
+ : Number(total);
+
+ if (list.value.length >= total || records.length < page.size) {
+ pageStatus.value = "nomore";
+ } else {
+ pageStatus.value = "loadmore";
+ page.current += 1;
+ }
+ } catch {
+ if (page.current === 1) list.value = [];
+ pageStatus.value = "loadmore";
+ uni.showToast({ title: `${pageTitle.value}鍔犺浇澶辫触`, icon: "none" });
+ } finally {
+ tableLoading.value = false;
+ }
+ };
+
+ const initBusinessType = async () => {
+ const fixed = getModuleListBusinessType(props.moduleKey);
+ businessType.value = fixed != null && fixed !== "" ? fixed : "";
+ try {
+ typeOptions.value = await fetchApprovalTemplateTypes();
+ } catch {
+ typeOptions.value = [];
+ }
+ };
+
+ const handleSearch = () => fetchList(true);
+ const handleReset = () => {
+ Object.assign(searchForm, createModuleSearchForm(props.moduleKey));
+ fetchList(true);
+ };
+ const loadMore = () => {
+ if (pageStatus.value === "loadmore") fetchList(false);
+ };
+ const goBack = () => uni.navigateBack();
+
+ const openDetail = item => {
+ if (!item?.id) return;
+ stashInstanceRow(item);
+ uni.navigateTo({
+ url: `${OA_NAV.approveListDetail}?id=${item.id}&from=business`,
+ });
+ };
+
+ const goEdit = item => {
+ if (!canEditBusinessInstanceRow(item)) {
+ uni.showToast({ title: "杩涜涓垨宸插畬鎴愮殑瀹℃壒涓嶅彲淇敼", icon: "none" });
+ return;
+ }
+ if (!item?.id) return;
+ uni.setStorageSync(EDIT_STORAGE_KEY, item);
+ stashInstanceRow(item);
+ uni.navigateTo({
+ url: `${OA_NAV.approveListApply}?id=${item.id}&moduleKey=${props.moduleKey}`,
+ });
+ };
+
+ const confirmDelete = item => {
+ if (!item?.id) return;
+ const title = item.title || item.templateName || item.instanceNo || "璇ュ鎵�";
+ uni.showModal({
+ title: "鍒犻櫎纭",
+ content: `纭畾瑕佸垹闄ゃ��${title}銆嶅悧锛熷垹闄ゅ悗涓嶅彲鎭㈠銆俙,
+ confirmText: "纭畾鍒犻櫎",
+ confirmColor: "#f56c6c",
+ success: async res => {
+ if (!res.confirm) return;
+ try {
+ await deleteApprovalInstance([item.id]);
+ uni.showToast({ title: "鍒犻櫎鎴愬姛", icon: "success" });
+ fetchList(true);
+ } catch {
+ uni.showToast({ title: "鍒犻櫎澶辫触", icon: "none" });
+ }
+ },
+ });
+ };
+
+ const handleAdd = () => {
+ uni.navigateTo({
+ url: `${OA_NAV.approveListTemplateSelect}?moduleKey=${props.moduleKey}`,
+ });
+ };
+
+ onMounted(() => {
+ calcListScrollHeight();
+ });
+
+ onShow(async () => {
+ calcListScrollHeight();
+ await initBusinessType();
+ fetchList(true);
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "@/styles/sales-common.scss";
+ @import "../_styles/oa-approval-list.scss";
+</style>
--
Gitblit v1.9.3