单据、业务中心、公告和工作通知

This commit is contained in:
2026-09-24 14:24:08 +08:00
parent 5e77d4fdb1
commit 892acc6d23
4 changed files with 80 additions and 60 deletions
+19 -10
View File
@@ -2,14 +2,20 @@
<view class="page edge"> <view class="page edge">
<fy-header :title="title" back /> <fy-header :title="title" back />
<fy-scroll class="edge-scroll" @refresh="load"> <fy-scroll class="edge-scroll" @refresh="load">
<view v-if="loading" class="empty">加载中…</view> <view v-if="loading" class="empty">
<view v-else-if="!rows.length" class="empty">暂无记录</view> <wd-skeleton theme="paragraph" />
</view>
<wd-status-tip v-else-if="!rows.length" image="content" tip="暂无记录" />
<view v-for="r in rows" :key="r.id || r.no" class="card item" @click="open(r)"> <view v-for="r in rows" :key="r.id || r.no" class="card item" @click="open(r)">
<view> <view class="main">
<text class="t">{{ r.title || r.no || r.typeLabel }}</text> <text class="t">{{ r.title || r.no || r.typeLabel }}</text>
<text class="s">{{ r.status || '' }} · {{ r.createdAt || r.date || '' }}</text> <text class="s">{{ r.createdAt || r.date || '' }}</text>
</view> </view>
<text class="amt">{{ r.amount != null ? '¥' + r.amount : '' }}</text> <view class="side">
<text v-if="r.amount != null" class="amt">¥{{ r.amount }}</text>
<wd-tag v-if="r.status" type="primary" plain>{{ r.status }}</wd-tag>
</view>
<text class="arr">›</text>
</view> </view>
</fy-scroll> </fy-scroll>
</view> </view>
@@ -54,9 +60,12 @@ export default {
</script> </script>
<style scoped> <style scoped>
.item { margin: 16rpx 24rpx; padding: 24rpx; display: flex; justify-content: space-between; } .item { margin: 16rpx 24rpx; padding: 24rpx; display: flex; align-items: center; }
.t { display: block; font-size: 28rpx; font-weight: 600; } .main { flex: 1; min-width: 0; }
.s { font-size: 22rpx; color: #6B7280; } .t { display: block; font-size: 28rpx; font-weight: 650; color: #111827; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.amt { color: #1677FF; font-weight: 700; } .s { display: block; margin-top: 8rpx; font-size: 24rpx; color: #6B7280; }
.empty { padding: 80rpx; text-align: center; color: #6B7280; } .side { display: flex; flex-direction: column; align-items: flex-end; gap: 8rpx; margin-left: 16rpx; }
.amt { color: #1677FF; font-weight: 700; font-size: 28rpx; }
.arr { color: #C4C9D4; font-size: 32rpx; margin-left: 8rpx; }
.empty { padding: 80rpx 40rpx; }
</style> </style>
+24 -26
View File
@@ -1,36 +1,33 @@
<template> <template>
<view class="page"> <view class="page edge">
<fy-header title="工作通知" back /> <fy-header title="工作通知" back />
<fy-scroll class="edge-scroll" @refresh="load">
<view class="bar"> <view class="bar">
<text>审批 · 协同 · 投标 · 用章 · 公告</text> <text>审批 · 协同 · 投标 · 用章 · 公告</text>
<text class="badge">{{ items.length }} 项待办</text> <text class="count">{{ items.length }} 项待办</text>
</view> </view>
<view v-if="!items.length" class="empty">暂无待办通知</view> <wd-status-tip v-if="!items.length" image="content" tip="暂无待办通知" />
<view v-for="t in items" :key="t.no || t.id" class="card item" @click="open(t)"> <view v-for="t in items" :key="t.no || t.id" class="card item" @click="open(t)">
<view class="stripe" /> <view class="main">
<view class="body"> <text class="type">{{ t.typeLabel || t.tag || t.type || '待办' }}</text>
<view class="top">
<text class="kind">{{ t.typeLabel || t.tag || t.type || '待办' }}</text>
<text class="time">{{ t.createdAt || t.time || '' }}</text>
</view>
<text class="title">{{ t.title || t.summary || t.no }}</text> <text class="title">{{ t.title || t.summary || t.no }}</text>
<text class="sub">{{ t.submitter || t.applicant || '' }}{{ t.dept ? ' · ' + t.dept : '' }}</text> <text class="sub">{{ metaOf(t) }}</text>
<view class="acts">
<text class="go">去办理</text>
</view>
</view> </view>
<text class="arr">›</text>
</view> </view>
</fy-scroll>
</view> </view>
</template> </template>
<script> <script>
import FyHeader from '../../components/fy-header.vue' import FyHeader from '../../components/fy-header.vue'
import FyScroll from '../../components/fy-scroll.vue'
import { store } from '../../utils/store.js' import { store } from '../../utils/store.js'
import { asList, fmtTime } from '../../utils/format.js' import { asList, fmtTime } from '../../utils/format.js'
import { oaGet } from '../../utils/api.js' import { oaGet } from '../../utils/api.js'
export default { export default {
components: { FyHeader }, components: { FyHeader, FyScroll },
data() { data() {
return { items: [] } return { items: [] }
}, },
@@ -51,6 +48,11 @@ export default {
const conv = store.conversations.find((c) => c.type === 'work') const conv = store.conversations.find((c) => c.type === 'work')
if (conv) conv.unread = 0 if (conv) conv.unread = 0
}, },
metaOf(t) {
const who = [t.submitter || t.applicant, t.dept].filter(Boolean).join(' · ')
const time = t.createdAt || t.time || ''
return [who, time].filter(Boolean).join(' · ')
},
open(t) { open(t) {
const no = t.no || t.id || '' const no = t.no || t.id || ''
if (String(t.source || t.type || '') === 'work' || t.taskId) { if (String(t.source || t.type || '') === 'work' || t.taskId) {
@@ -64,16 +66,12 @@ export default {
</script> </script>
<style scoped> <style scoped>
.bar { margin: 16rpx 24rpx; background: #F3F6FB; border-radius: 16rpx; padding: 16rpx 20rpx; display: flex; justify-content: space-between; font-size: 22rpx; color: #4B5563; } .bar { margin: 16rpx 24rpx 8rpx; padding: 0 8rpx; display: flex; justify-content: space-between; align-items: center; font-size: 22rpx; color: #6B7280; }
.item { margin: 0 24rpx 20rpx; display: flex; overflow: hidden; } .count { color: #1677FF; font-weight: 600; }
.stripe { width: 8rpx; background: #1677FF; } .item { margin: 16rpx 24rpx; padding: 24rpx; display: flex; align-items: center; }
.body { flex: 1; padding: 24rpx; } .main { flex: 1; min-width: 0; }
.top { display: flex; justify-content: space-between; } .type { display: inline-block; font-size: 22rpx; color: #1677FF; background: #E8F1FF; border-radius: 8rpx; padding: 2rpx 10rpx; }
.kind { font-size: 28rpx; font-weight: 700; } .title { display: block; margin-top: 10rpx; font-size: 28rpx; font-weight: 650; color: #111827; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.time { font-size: 22rpx; color: #6B7280; } .sub { display: block; margin-top: 6rpx; font-size: 24rpx; color: #6B7280; }
.title { display: block; font-size: 28rpx; font-weight: 600; margin: 12rpx 0 6rpx; } .arr { color: #C4C9D4; font-size: 32rpx; margin-left: 8rpx; }
.sub { font-size: 22rpx; color: #6B7280; }
.acts { margin-top: 16rpx; }
.go { color: #1677FF; font-size: 24rpx; font-weight: 600; }
.empty { text-align: center; color: #6B7280; padding: 80rpx; }
</style> </style>
+28 -18
View File
@@ -1,23 +1,28 @@
<template> <template>
<view class="page"> <view class="page edge">
<fy-header :title="title" back /> <fy-header :title="title" back />
<view v-if="!allowed" class="deny">当前账号没有「{{ title }}」查看权限</view> <view v-if="!allowed" class="deny">当前账号没有「{{ title }}」查看权限</view>
<view v-else> <fy-scroll v-else class="edge-scroll" @refresh="load">
<scroll-view scroll-x class="tabs"> <wd-tabs v-if="tabs.length" v-model="tab" slidable="always" @change="onTab">
<text v-for="t in tabs" :key="t.id" class="tab" :class="{ on: tab === t.id }" @click="switchTab(t)">{{ t.title }}</text> <wd-tab v-for="t in tabs" :key="t.id" :title="t.title" :name="t.id" />
</scroll-view> </wd-tabs>
<view v-if="loading" class="empty">加载中…</view> <view v-if="loading" class="empty">
<view v-else-if="!rows.length" class="empty">暂无数据</view> <wd-skeleton theme="paragraph" />
<view v-for="r in rows" :key="r.id || r.no || r.code" class="card item">
<text class="t">{{ titleOf(r) }}</text>
<text class="s">{{ subOf(r) }}</text>
</view> </view>
</view> <wd-status-tip v-else-if="!rows.length" image="content" tip="暂无数据" />
<view v-for="r in rows" :key="r.id || r.no || r.code" class="card item">
<view class="main">
<text class="t">{{ titleOf(r) }}</text>
<text v-if="subOf(r)" class="s">{{ subOf(r) }}</text>
</view>
</view>
</fy-scroll>
</view> </view>
</template> </template>
<script> <script>
import FyHeader from '../../components/fy-header.vue' import FyHeader from '../../components/fy-header.vue'
import FyScroll from '../../components/fy-scroll.vue'
import { store } from '../../utils/store.js' import { store } from '../../utils/store.js'
import { ERP_MODULES, ERP_TABS } from '../../utils/catalog.js' import { ERP_MODULES, ERP_TABS } from '../../utils/catalog.js'
import { asList, firstText } from '../../utils/format.js' import { asList, firstText } from '../../utils/format.js'
@@ -25,7 +30,7 @@ import { oaGet } from '../../utils/api.js'
import { canMenu } from '../../utils/perms.js' import { canMenu } from '../../utils/perms.js'
export default { export default {
components: { FyHeader }, components: { FyHeader, FyScroll },
data() { data() {
return { id: '', tab: '', rows: [], loading: false } return { id: '', tab: '', rows: [], loading: false }
}, },
@@ -45,6 +50,12 @@ export default {
if (this.allowed) this.load() if (this.allowed) this.load()
}, },
methods: { methods: {
onTab(e) {
const id = (e && e.name) || this.tab
if (id === this.tab && this.rows.length) return
this.tab = id
this.load()
},
switchTab(t) { switchTab(t) {
this.tab = t.id this.tab = t.id
this.load() this.load()
@@ -110,11 +121,10 @@ export default {
</script> </script>
<style scoped> <style scoped>
.tabs { white-space: nowrap; padding: 16rpx 16rpx 0; }
.tab { display: inline-block; padding: 12rpx 20rpx; font-size: 24rpx; color: #6B7280; }
.tab.on { color: #1677FF; font-weight: 700; border-bottom: 4rpx solid #1677FF; }
.item { margin: 16rpx 24rpx; padding: 24rpx; } .item { margin: 16rpx 24rpx; padding: 24rpx; }
.t { display: block; font-weight: 600; font-size: 28rpx; } .main { min-width: 0; }
.s { font-size: 22rpx; color: #6B7280; } .t { display: block; font-weight: 650; font-size: 28rpx; color: #111827; }
.empty, .deny { padding: 80rpx 40rpx; text-align: center; color: #6B7280; } .s { display: block; margin-top: 8rpx; font-size: 24rpx; color: #6B7280; }
.empty { padding: 40rpx 28rpx; }
.deny { padding: 120rpx 40rpx; text-align: center; color: #6B7280; font-size: 28rpx; }
</style> </style>
+9 -6
View File
@@ -4,8 +4,11 @@
<fy-scroll class="edge-scroll" @refresh="load"> <fy-scroll class="edge-scroll" @refresh="load">
<wd-status-tip v-if="!rows.length" image="content" tip="暂无公告" /> <wd-status-tip v-if="!rows.length" image="content" tip="暂无公告" />
<view v-for="r in rows" :key="r.id" class="card item"> <view v-for="r in rows" :key="r.id" class="card item">
<view class="top">
<text class="chip">公告</text>
<text class="s">{{ r.createdAt || r.publishAt || '' }}</text>
</view>
<text class="t">{{ r.title }}</text> <text class="t">{{ r.title }}</text>
<text class="s">{{ r.createdAt || r.publishAt || '' }}</text>
<text class="c">{{ r.content || r.summary || '' }}</text> <text class="c">{{ r.content || r.summary || '' }}</text>
</view> </view>
</fy-scroll> </fy-scroll>
@@ -32,9 +35,9 @@ export default {
</script> </script>
<style scoped> <style scoped>
.item { margin: 16rpx 28rpx; padding: 28rpx; } .item { margin: 16rpx 24rpx; padding: 28rpx 24rpx; }
.t { display: block; font-weight: 650; font-size: 30rpx; line-height: 1.45; } .top { display: flex; align-items: center; justify-content: space-between; }
.s { display: block; font-size: 24rpx; color: #6B7280; margin-top: 10rpx; } .s { font-size: 22rpx; color: #9CA3AF; }
.c { display: block; font-size: 28rpx; margin-top: 16rpx; color: #374151; line-height: 1.65; } .t { display: block; margin-top: 16rpx; font-weight: 650; font-size: 30rpx; line-height: 1.45; color: #111827; }
.empty { padding: 120rpx 40rpx; text-align: center; color: #6B7280; font-size: 28rpx; } .c { display: block; font-size: 26rpx; margin-top: 12rpx; color: #4B5563; line-height: 1.65; }
</style> </style>