|
|
@@ -0,0 +1,299 @@
|
|
|
+<template>
|
|
|
+ <view class="page">
|
|
|
+ <view class="search-wrap">
|
|
|
+ <input
|
|
|
+ class="search"
|
|
|
+ v-model="searchKeyword"
|
|
|
+ confirm-type="search"
|
|
|
+ placeholder="请输入审批单名称"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <scroll-view scroll-x class="tabs" :show-scrollbar="false">
|
|
|
+ <view
|
|
|
+ class="tab"
|
|
|
+ :class="{ active: activeGroupId === '' }"
|
|
|
+ @click="activeGroupId = ''"
|
|
|
+ >
|
|
|
+ 全部
|
|
|
+ </view>
|
|
|
+ <view
|
|
|
+ v-for="g in groupOptions"
|
|
|
+ :key="g.id"
|
|
|
+ class="tab"
|
|
|
+ :class="{ active: activeGroupId === g.id }"
|
|
|
+ @click="activeGroupId = g.id"
|
|
|
+ >
|
|
|
+ {{ g.groupName }}
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
+
|
|
|
+ <view class="hint">选择一个审批单即可发起</view>
|
|
|
+
|
|
|
+ <view v-if="loading" class="state">加载中…</view>
|
|
|
+ <view v-else-if="!displayGroups.length" class="state">
|
|
|
+ 暂无可发起的审批模板
|
|
|
+ </view>
|
|
|
+ <view v-else class="body">
|
|
|
+ <view v-for="sec in displayGroups" :key="sec.id" class="section">
|
|
|
+ <view class="section-hd">
|
|
|
+ <text class="section-name">{{ sec.name }}</text>
|
|
|
+ <text class="section-meta">{{ sec.apps.length }} 个表单</text>
|
|
|
+ </view>
|
|
|
+ <view
|
|
|
+ v-for="row in sec.apps"
|
|
|
+ :key="row.id"
|
|
|
+ class="card"
|
|
|
+ @click="onPick(row)"
|
|
|
+ >
|
|
|
+ <view class="card-main">
|
|
|
+ <view class="card-icon">
|
|
|
+ <image
|
|
|
+ v-if="row.iconUrl"
|
|
|
+ class="card-icon__img"
|
|
|
+ :src="row.iconUrl"
|
|
|
+ mode="aspectFill"
|
|
|
+ />
|
|
|
+ <text v-else class="card-icon__text">{{
|
|
|
+ (row.appName || '审').slice(0, 1)
|
|
|
+ }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="card-body">
|
|
|
+ <text class="card-name">{{ row.appName }}</text>
|
|
|
+ <text class="card-desc">{{ row.description || '点击发起审批' }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="card-btn" @click.stop="onPick(row)">发起</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { fetchOaGroupList, fetchOaTemplates } from '../../../utils/oa-api.js'
|
|
|
+import { getToken } from '../../../utils/auth.js'
|
|
|
+
|
|
|
+export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ loading: false,
|
|
|
+ templates: [],
|
|
|
+ groupOptions: [],
|
|
|
+ searchKeyword: '',
|
|
|
+ activeGroupId: ''
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ displayGroups() {
|
|
|
+ const kw = (this.searchKeyword || '').trim().toLowerCase()
|
|
|
+ let list = [...(this.templates || [])]
|
|
|
+ if (this.activeGroupId) {
|
|
|
+ list = list.filter((a) => a.groupId === this.activeGroupId)
|
|
|
+ }
|
|
|
+ if (kw) {
|
|
|
+ list = list.filter((a) =>
|
|
|
+ String(a.appName || '')
|
|
|
+ .toLowerCase()
|
|
|
+ .includes(kw)
|
|
|
+ )
|
|
|
+ }
|
|
|
+ const bucket = new Map()
|
|
|
+ for (const g of this.groupOptions) {
|
|
|
+ bucket.set(g.id, { id: g.id, name: g.groupName, apps: [] })
|
|
|
+ }
|
|
|
+ const ungrouped = { id: '_ungrouped', name: '未分组', apps: [] }
|
|
|
+ for (const app of list) {
|
|
|
+ if (app.groupId && bucket.has(app.groupId)) {
|
|
|
+ bucket.get(app.groupId).apps.push(app)
|
|
|
+ } else {
|
|
|
+ ungrouped.apps.push(app)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (this.activeGroupId) {
|
|
|
+ const one = bucket.get(this.activeGroupId)
|
|
|
+ return one && one.apps.length ? [one] : []
|
|
|
+ }
|
|
|
+ const sections = []
|
|
|
+ for (const g of this.groupOptions) {
|
|
|
+ const sec = bucket.get(g.id)
|
|
|
+ if (sec && sec.apps.length) sections.push(sec)
|
|
|
+ }
|
|
|
+ if (ungrouped.apps.length) sections.push(ungrouped)
|
|
|
+ return sections
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onShow() {
|
|
|
+ if (!getToken()) {
|
|
|
+ uni.showToast({ title: '请先登录', icon: 'none' })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.load()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ async load() {
|
|
|
+ this.loading = true
|
|
|
+ try {
|
|
|
+ const [groups, templates] = await Promise.all([
|
|
|
+ fetchOaGroupList(),
|
|
|
+ fetchOaTemplates()
|
|
|
+ ])
|
|
|
+ this.groupOptions = (groups?.list || groups || []).map((g) => ({
|
|
|
+ id: g.id,
|
|
|
+ groupName: g.groupName || g.name || '分组'
|
|
|
+ }))
|
|
|
+ this.templates = templates?.list || templates || []
|
|
|
+ } catch (e) {
|
|
|
+ uni.showToast({ title: (e && e.message) || '加载失败', icon: 'none' })
|
|
|
+ this.templates = []
|
|
|
+ this.groupOptions = []
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onPick(row) {
|
|
|
+ if (!row || !row.id) return
|
|
|
+ uni.navigateTo({
|
|
|
+ url: '/pages/oa/start/webview?appId=' + encodeURIComponent(row.id)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.page {
|
|
|
+ min-height: 100vh;
|
|
|
+ padding-bottom: 24rpx;
|
|
|
+ background: #f5f8f7;
|
|
|
+}
|
|
|
+.search-wrap {
|
|
|
+ padding: 20rpx 24rpx 8rpx;
|
|
|
+ background: #fff;
|
|
|
+}
|
|
|
+.search {
|
|
|
+ height: 72rpx;
|
|
|
+ padding: 0 24rpx;
|
|
|
+ border-radius: 16rpx;
|
|
|
+ background: #f5f8f7;
|
|
|
+ font-size: 28rpx;
|
|
|
+}
|
|
|
+.tabs {
|
|
|
+ white-space: nowrap;
|
|
|
+ padding: 16rpx 16rpx 8rpx;
|
|
|
+ background: #fff;
|
|
|
+}
|
|
|
+.tab {
|
|
|
+ display: inline-block;
|
|
|
+ margin-right: 16rpx;
|
|
|
+ padding: 12rpx 24rpx;
|
|
|
+ border-radius: 999rpx;
|
|
|
+ background: #f5f8f7;
|
|
|
+ color: #6b7b74;
|
|
|
+ font-size: 26rpx;
|
|
|
+}
|
|
|
+.tab.active {
|
|
|
+ color: #fff;
|
|
|
+ background: #0a9f6d;
|
|
|
+}
|
|
|
+.hint {
|
|
|
+ padding: 16rpx 24rpx;
|
|
|
+ color: #6b7b74;
|
|
|
+ font-size: 24rpx;
|
|
|
+}
|
|
|
+.state {
|
|
|
+ padding: 80rpx 24rpx;
|
|
|
+ text-align: center;
|
|
|
+ color: #6b7b74;
|
|
|
+ font-size: 28rpx;
|
|
|
+}
|
|
|
+.body {
|
|
|
+ padding: 0 24rpx;
|
|
|
+}
|
|
|
+.section {
|
|
|
+ margin-bottom: 24rpx;
|
|
|
+}
|
|
|
+.section-hd {
|
|
|
+ display: flex;
|
|
|
+ align-items: baseline;
|
|
|
+ gap: 12rpx;
|
|
|
+ margin-bottom: 16rpx;
|
|
|
+}
|
|
|
+.section-name {
|
|
|
+ font-size: 30rpx;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #1f2a26;
|
|
|
+}
|
|
|
+.section-meta {
|
|
|
+ font-size: 22rpx;
|
|
|
+ color: #6b7b74;
|
|
|
+}
|
|
|
+.card {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ gap: 16rpx;
|
|
|
+ margin-bottom: 16rpx;
|
|
|
+ padding: 24rpx;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ background: #fff;
|
|
|
+ box-shadow: 0 8rpx 24rpx rgba(8, 127, 87, 0.06);
|
|
|
+}
|
|
|
+.card-main {
|
|
|
+ display: flex;
|
|
|
+ flex: 1;
|
|
|
+ gap: 20rpx;
|
|
|
+ align-items: center;
|
|
|
+ min-width: 0;
|
|
|
+}
|
|
|
+.card-icon {
|
|
|
+ width: 72rpx;
|
|
|
+ height: 72rpx;
|
|
|
+ border-radius: 16rpx;
|
|
|
+ overflow: hidden;
|
|
|
+ background: #e9f9f2;
|
|
|
+ flex-shrink: 0;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+.card-icon__img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+.card-icon__text {
|
|
|
+ color: #0a9f6d;
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: 700;
|
|
|
+}
|
|
|
+.card-body {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 6rpx;
|
|
|
+}
|
|
|
+.card-name {
|
|
|
+ font-size: 30rpx;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1f2a26;
|
|
|
+}
|
|
|
+.card:active {
|
|
|
+ opacity: 0.92;
|
|
|
+}
|
|
|
+.card-desc {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #6b7b74;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+.card-btn {
|
|
|
+ flex-shrink: 0;
|
|
|
+ padding: 12rpx 28rpx;
|
|
|
+ border-radius: 999rpx;
|
|
|
+ background: #0a9f6d;
|
|
|
+ color: #fff;
|
|
|
+ font-size: 26rpx;
|
|
|
+}
|
|
|
+</style>
|