审批管理
This commit is contained in:
parent
6da442b49e
commit
d7d7661a33
|
|
@ -24,3 +24,6 @@ export * from './customer';
|
|||
|
||||
// 导入通用 API
|
||||
export * from './common';
|
||||
|
||||
// 导入审批相关 API
|
||||
export * from './verify';
|
||||
18
api/verify.js
Normal file
18
api/verify.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// 审批相关 API
|
||||
export const getVerifyList = (params = {}) => {
|
||||
return uni.$uv.http.get('/bst/verify/list', {
|
||||
params: {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
orderByColumn: 'createTime',
|
||||
isAsc: 'descending',
|
||||
bstType: 'UPDATE_TASK',
|
||||
...params
|
||||
},
|
||||
custom: {
|
||||
auth: true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
146
components/index/Workbench.vue
Normal file
146
components/index/Workbench.vue
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
<template>
|
||||
<scroll-view class="workbench-scroll" scroll-y>
|
||||
<view class="workbench-card">
|
||||
<view class="header">
|
||||
<text class="title">工作台</text>
|
||||
<view class="search-box" @click="goToSearch">
|
||||
<text class="search-icon">🔍</text>
|
||||
<text class="placeholder">搜索</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="grid">
|
||||
<view
|
||||
class="grid-item"
|
||||
v-for="item in items"
|
||||
:key="item.key"
|
||||
@click="handleClick(item)"
|
||||
>
|
||||
<view class="icon-wrapper">
|
||||
<text class="icon-text">{{ item.icon }}</text>
|
||||
</view>
|
||||
<text class="item-text">{{ item.text }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const items = ref([
|
||||
{ key: 'verify', text: '审批管理', icon: '📝' },
|
||||
{ key: 'customer', text: '客户管理', icon: '👤' },
|
||||
{ key: 'project', text: '项目管理', icon: '📚' },
|
||||
{ key: 'task', text: '任务管理', icon: '🗂️' },
|
||||
{ key: 'schedule', text: '日程管理', icon: '🗓️' },
|
||||
{ key: 'contact', text: '通讯录', icon: '📇' },
|
||||
{ key: 'notice', text: '公告管理', icon: '📢' },
|
||||
{ key: 'wechat', text: '工作微信', icon: '🤖' }
|
||||
]);
|
||||
|
||||
const goToSearch = () => {
|
||||
// 预留搜索页
|
||||
};
|
||||
|
||||
const handleClick = (item) => {
|
||||
if (item.key === 'verify') {
|
||||
// 跳转到延期审核列表
|
||||
uni.navigateTo({
|
||||
url: '/pages/verify/list/index?bstType=UPDATE_TASK'
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (item.key === 'task') {
|
||||
uni.switchTab({ url: '/pages/index/index' });
|
||||
return;
|
||||
}
|
||||
if (item.key === 'customer') {
|
||||
uni.switchTab({ url: '/pages/index/index' });
|
||||
return;
|
||||
}
|
||||
// 其他入口占位
|
||||
uni.showToast({ title: '开发中', icon: 'none' });
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.workbench-scroll {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.workbench-card {
|
||||
margin: 16px;
|
||||
padding: 16px;
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 10px;
|
||||
background: #f5f6f7;
|
||||
border-radius: 16px;
|
||||
}
|
||||
.search-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
.placeholder {
|
||||
font-size: 12px;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 16px 10px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.grid-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
border-radius: 50%;
|
||||
background: #2885ff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.icon-text {
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.item-text {
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
|
@ -15,6 +15,12 @@
|
|||
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/verify/list/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "审批管理"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/index",
|
||||
"style": {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@
|
|||
<!-- 月度考核(排行榜) -->
|
||||
<RankingBoard v-else-if="admin_bst && value === rankingIndex" />
|
||||
|
||||
|
||||
<!-- 工作台 -->
|
||||
<Workbench v-else-if="value === workIndex" />
|
||||
|
||||
<!-- 其他内容(底部导航栏未选中客户管理时显示) -->
|
||||
<template v-else>
|
||||
<!-- 日程编辑 -->
|
||||
|
|
@ -32,6 +36,7 @@
|
|||
|
||||
<!-- 消息内容 -->
|
||||
<MessageContent v-if="topTabValue === 3" />
|
||||
|
||||
</template>
|
||||
</view>
|
||||
|
||||
|
|
@ -69,6 +74,7 @@ import MessageContent from '@/components/index/MessageContent.vue';
|
|||
import CustomerManagement from '@/components/customer/CustomerManagement.vue';
|
||||
import My from '@/components/my/My.vue';
|
||||
import RankingBoard from '@/components/index/RankingBoard.vue';
|
||||
import Workbench from '@/components/index/Workbench.vue';
|
||||
|
||||
|
||||
// 顶部tabs选项
|
||||
|
|
@ -77,6 +83,7 @@ const topTabs = [
|
|||
{ name: '内容看板', value: 1 },
|
||||
{ name: '待办事项', value: 2 },
|
||||
{ name: '消息内容', value: 3 }
|
||||
|
||||
];
|
||||
// 默认显示内容看板
|
||||
const topTabValue = ref(1);
|
||||
|
|
@ -150,10 +157,11 @@ const admin_bst=ref(false);
|
|||
const rankingIndex = computed(() => 3);
|
||||
const customerManagementIndex = computed(() => 4);
|
||||
const myIndex = computed(() => 2);
|
||||
const workIndex = computed(() => 1);
|
||||
|
||||
// 判断是否显示顶部Tabs栏
|
||||
const shouldShowTopTabs = computed(() => {
|
||||
if (value.value === customerManagementIndex.value || value.value === myIndex.value) {
|
||||
if (value.value === customerManagementIndex.value || value.value === myIndex.value||value.value === workIndex.value) {
|
||||
return false;
|
||||
}
|
||||
if (admin_bst.value && value.value === rankingIndex.value) {
|
||||
|
|
|
|||
242
pages/verify/list/index.vue
Normal file
242
pages/verify/list/index.vue
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
<template>
|
||||
<view class="verify-page">
|
||||
<view class="tabs-wrapper">
|
||||
<uv-tabs :list="tabs" :current="currentTab" @click="onTabClick"></uv-tabs>
|
||||
</view>
|
||||
|
||||
<scroll-view class="list-scroll" scroll-y @scrolltolower="loadMore">
|
||||
<view class="card" v-for="item in displayList" :key="item.id" @click="goDetail(item)">
|
||||
<view class="card-header">
|
||||
<view class="left">
|
||||
<text class="badge">申请延期</text>
|
||||
<text class="sub">所属项目 · {{ item.projectName || '—' }}</text>
|
||||
</view>
|
||||
<view class="right">
|
||||
<uv-tags :text="statusText(item.status)" :type="statusType(item.status)" size="mini"></uv-tags>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card-body">
|
||||
<text class="remark" v-if="item.createRemark">{{ item.createRemark }}</text>
|
||||
<view class="row">
|
||||
<text class="label">截止时间:</text>
|
||||
<text class="value">{{ item.expireTime || '—' }}</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">申请人:</text>
|
||||
<text class="value">{{ item.createName || '—' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card-footer">
|
||||
<uv-button
|
||||
v-if="item.status === '3'"
|
||||
type="primary"
|
||||
size="small"
|
||||
text="去处理"
|
||||
@click.stop="goHandle(item)"
|
||||
/>
|
||||
<uv-button
|
||||
v-else
|
||||
type="info"
|
||||
size="small"
|
||||
:text="statusText(item.status)"
|
||||
:plain="true"
|
||||
:disabled="true"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="empty" v-if="!loading && displayList.length === 0">
|
||||
<text>暂无数据</text>
|
||||
</view>
|
||||
<view class="loading" v-if="loading">
|
||||
<text>加载中...</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { getVerifyList } from '@/api';
|
||||
|
||||
const tabs = [
|
||||
{ name: '待我处理', value: 'pending' },
|
||||
{ name: '我已处理', value: 'done' }
|
||||
];
|
||||
const currentTab = ref(0);
|
||||
|
||||
const pageNum = ref(1);
|
||||
const pageSize = ref(20);
|
||||
const total = ref(0);
|
||||
const rawList = ref([]);
|
||||
const loading = ref(false);
|
||||
const bstType = ref('UPDATE_TASK');
|
||||
|
||||
function statusText(s) {
|
||||
if (s === '1') return '已通过';
|
||||
if (s === '2') return '已驳回';
|
||||
return '待处理';
|
||||
}
|
||||
function statusType(s) {
|
||||
if (s === '1') return 'success';
|
||||
if (s === '2') return 'error';
|
||||
return 'warning';
|
||||
}
|
||||
|
||||
const displayList = computed(() => {
|
||||
if (currentTab.value === 0) {
|
||||
return rawList.value.filter(i => String(i.status) === '3');
|
||||
}
|
||||
return rawList.value.filter(i => String(i.status) !== '3');
|
||||
});
|
||||
|
||||
const safeParseExpire = (dataField) => {
|
||||
if (!dataField) return '';
|
||||
try {
|
||||
if (typeof dataField === 'string') {
|
||||
return JSON.parse(dataField)?.expireTime || '';
|
||||
}
|
||||
return dataField.expireTime || '';
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
const fetchList = async () => {
|
||||
if (loading.value) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getVerifyList({
|
||||
pageNum: pageNum.value,
|
||||
pageSize: pageSize.value,
|
||||
orderByColumn: 'createTime',
|
||||
isAsc: 'descending',
|
||||
bstType: bstType.value
|
||||
});
|
||||
const rows = res?.rows || [];
|
||||
total.value = res?.total || 0;
|
||||
const mapped = rows.map(r => ({
|
||||
...r,
|
||||
expireTime: safeParseExpire(r.data)
|
||||
}));
|
||||
if (pageNum.value === 1) {
|
||||
rawList.value = mapped;
|
||||
} else {
|
||||
rawList.value = rawList.value.concat(mapped);
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const loadMore = () => {
|
||||
if (rawList.value.length >= total.value) return;
|
||||
pageNum.value += 1;
|
||||
fetchList();
|
||||
};
|
||||
|
||||
const onTabClick = (tab) => {
|
||||
currentTab.value = tab.index;
|
||||
};
|
||||
|
||||
const goHandle = (item) => {
|
||||
// 预留处理入口
|
||||
uni.showToast({ title: '去处理', icon: 'none' });
|
||||
};
|
||||
|
||||
const goDetail = (item) => {
|
||||
// 可跳转到任务详情或审批详情
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// 读取路由参数
|
||||
const pages = getCurrentPages();
|
||||
const currentPage = pages[pages.length - 1];
|
||||
const options = currentPage?.options || {};
|
||||
if (options.bstType) {
|
||||
bstType.value = options.bstType;
|
||||
}
|
||||
fetchList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.verify-page {
|
||||
|
||||
width: 100%;
|
||||
|
||||
background: #f5f5f5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.tabs-wrapper {
|
||||
background: #fff;
|
||||
}
|
||||
.list-scroll {
|
||||
flex: 1;
|
||||
}
|
||||
.card {
|
||||
background: #fff;
|
||||
margin: 12px 12px 0 12px;
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.badge {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
.sub {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
.card-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.remark {
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
.label {
|
||||
width: 70px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.value {
|
||||
color: #333;
|
||||
}
|
||||
.card-footer {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.empty, .loading {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
padding: 16px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user