<template>
|
<view class="oa-page sales-account">
|
<PageHeader :title="pageConfig.title"
|
@back="goBack" />
|
<view class="search-section">
|
<view class="search-bar">
|
<view class="search-input">
|
<up-input v-model="keyword"
|
class="search-text"
|
:placeholder="`搜索${pageConfig.title}`"
|
clearable
|
@change="handleSearch" />
|
</view>
|
<view class="filter-button"
|
@click="handleSearch">
|
<up-icon name="search"
|
size="24"
|
color="#999" />
|
</view>
|
</view>
|
</view>
|
|
<scroll-view class="list-scroll"
|
scroll-y
|
:show-scrollbar="false">
|
<view v-if="displayList.length"
|
class="ledger-list">
|
<view v-for="item in displayList"
|
:key="item.id"
|
class="ledger-item"
|
@click="openDetail(item)">
|
<view class="item-header">
|
<view class="item-left">
|
<view class="document-icon">
|
<up-icon name="file-text"
|
size="16"
|
color="#ffffff" />
|
</view>
|
<text class="item-id">{{ item.summary || item.applicantName || pageConfig.title }}</text>
|
</view>
|
<u-tag :type="getStatusMeta(item.status).type"
|
:text="getStatusMeta(item.status).text" />
|
</view>
|
<up-divider />
|
<view class="item-details">
|
<view v-for="field in pageConfig.fields"
|
:key="field.prop"
|
class="detail-row">
|
<text class="detail-label">{{ field.label }}</text>
|
<text class="detail-value">{{ item[field.prop] || "-" }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">申请人</text>
|
<text class="detail-value">{{ item.applicantName || "-" }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">申请时间</text>
|
<text class="detail-value">{{ item.createTime || "-" }}</text>
|
</view>
|
</view>
|
</view>
|
</view>
|
<view v-else
|
class="empty-wrap">
|
<up-empty mode="list"
|
:text="`暂无${pageConfig.title}数据`" />
|
</view>
|
</scroll-view>
|
|
<view class="footer-add">
|
<up-button type="primary"
|
text="新增"
|
@click="handleAdd" />
|
</view>
|
</view>
|
</template>
|
|
<script setup>
|
import { computed, ref } from "vue";
|
import { onShow } from "@dcloudio/uni-app";
|
import PageHeader from "@/components/PageHeader.vue";
|
import { ensureList, saveList } from "../_utils/oaStorage.js";
|
import { getStatusMeta } from "../_utils/oaPageRegistry.js";
|
import { showToast } from "../_utils/oaUi.js";
|
|
const props = defineProps({
|
pageKey: {
|
type: String,
|
required: true,
|
},
|
pageConfig: {
|
type: Object,
|
required: true,
|
},
|
});
|
|
const keyword = ref("");
|
const list = ref([]);
|
|
const displayList = computed(() => {
|
const kw = keyword.value.trim();
|
if (!kw) return list.value;
|
return list.value.filter(item => {
|
const text = [
|
item.summary,
|
item.applicantName,
|
item.deptName,
|
...props.pageConfig.fields.map(f => item[f.prop]),
|
]
|
.filter(Boolean)
|
.join(" ");
|
return text.includes(kw);
|
});
|
});
|
|
const loadData = () => {
|
list.value = ensureList(
|
props.pageConfig.storageKey,
|
props.pageConfig.mockRows || []
|
);
|
};
|
|
const handleSearch = () => {
|
/* 关键字由 computed 过滤 */
|
};
|
|
const goBack = () => {
|
uni.navigateBack();
|
};
|
|
const openDetail = item => {
|
showToast(`查看:${item.summary || props.pageConfig.title}`);
|
};
|
|
const handleAdd = () => {
|
const row = {
|
...(props.pageConfig.mockRows?.[0] || {}),
|
id: `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
applicantName: "当前用户",
|
status: "pending",
|
createTime: new Date().toISOString().slice(0, 19).replace("T", " "),
|
summary: `新建${props.pageConfig.title}`,
|
};
|
list.value = [row, ...list.value];
|
saveList(props.pageConfig.storageKey, list.value);
|
showToast("已新增(本地示例)", "success");
|
};
|
|
onShow(() => {
|
loadData();
|
});
|
</script>
|
|
<style scoped lang="scss">
|
@import "@/styles/sales-common.scss";
|
|
.oa-page {
|
display: flex;
|
flex-direction: column;
|
min-height: 100vh;
|
}
|
|
.list-scroll {
|
flex: 1;
|
height: 0;
|
padding-bottom: 80px;
|
}
|
|
.empty-wrap {
|
padding: 48px 20px;
|
}
|
|
.footer-add {
|
position: fixed;
|
left: 0;
|
right: 0;
|
bottom: 0;
|
padding: 12px 20px calc(12px + env(safe-area-inset-bottom));
|
background: #fff;
|
box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.06);
|
}
|
</style>
|