feat(approval): 员工端内部协调单新建/编辑与审批中心入口 (#23)
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
import request from '@/utils/request';
|
||||||
|
import type { ApiResult } from '@/api';
|
||||||
|
import type { ApprovalOrganization } from '../model';
|
||||||
|
import { THINK_API_URL } from '@/config/setting';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用组织(内部协调单受理部门选择)
|
||||||
|
*/
|
||||||
|
export async function getOrganizations() {
|
||||||
|
const res = await request.get<ApiResult<ApprovalOrganization[]>>(
|
||||||
|
THINK_API_URL + '/approval/directory/organizations'
|
||||||
|
);
|
||||||
|
if (res.data.code === 0 && res.data.data) {
|
||||||
|
return res.data.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.data.message));
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import request from '@/utils/request';
|
|||||||
import type { ApiResult, PageResult } from '@/api';
|
import type { ApiResult, PageResult } from '@/api';
|
||||||
import type {
|
import type {
|
||||||
ApprovalCompletionForm,
|
ApprovalCompletionForm,
|
||||||
|
ApprovalCoordinationForm,
|
||||||
ApprovalInstance,
|
ApprovalInstance,
|
||||||
ApprovalItem,
|
ApprovalItem,
|
||||||
ApprovalMineParam,
|
ApprovalMineParam,
|
||||||
@@ -23,6 +24,20 @@ export async function saveCompletion(data: ApprovalCompletionForm) {
|
|||||||
return Promise.reject(new Error(res.data.message));
|
return Promise.reject(new Error(res.data.message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新建或保存内部协调单草稿,返回实例id
|
||||||
|
*/
|
||||||
|
export async function saveCoordination(data: ApprovalCoordinationForm) {
|
||||||
|
const res = await request.post<ApiResult<number>>(
|
||||||
|
THINK_API_URL + '/approval/instance/coordination',
|
||||||
|
data
|
||||||
|
);
|
||||||
|
if (res.data.code === 0) {
|
||||||
|
return res.data.data as number;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.data.message));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交或重提
|
* 提交或重提
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -128,6 +128,17 @@ export interface ApprovalCompletionForm {
|
|||||||
serviceContent: string;
|
serviceContent: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内部协调单草稿参数
|
||||||
|
*/
|
||||||
|
export interface ApprovalCoordinationForm {
|
||||||
|
id?: number;
|
||||||
|
acceptOrgId?: number;
|
||||||
|
title: string;
|
||||||
|
content: string;
|
||||||
|
remark?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 我发起的查询参数
|
* 我发起的查询参数
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -232,13 +232,19 @@ async function load() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function editAndResubmit() {
|
function editAndResubmit() {
|
||||||
if (detail.value?.docType !== 'completion') {
|
const docType = detail.value?.docType;
|
||||||
|
const page =
|
||||||
|
docType === 'completion'
|
||||||
|
? 'form-completion'
|
||||||
|
: docType === 'coordination'
|
||||||
|
? 'form-coordination'
|
||||||
|
: '';
|
||||||
|
if (!page) {
|
||||||
showError('该单据类型暂不支持在线修改', null);
|
showError('该单据类型暂不支持在线修改', null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url:
|
url: `/packageApproval/pages/${page}?mode=edit&id=` + instanceId.value
|
||||||
'/packageApproval/pages/form-completion?mode=edit&id=' + instanceId.value
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,575 @@
|
|||||||
|
<template>
|
||||||
|
<view class="form-page">
|
||||||
|
<uni-card :border="false" :padding="'16px'" title="基本信息">
|
||||||
|
<view class="auto-row">
|
||||||
|
<text class="label">经办人</text>
|
||||||
|
<text class="value">{{ reporterName || '—' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="auto-row">
|
||||||
|
<text class="label">报送部门</text>
|
||||||
|
<text class="value">{{ reportOrgName || '—' }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="field">
|
||||||
|
<text class="field-label">受理部门<text class="required">*</text></text>
|
||||||
|
<picker
|
||||||
|
:range="organizationLabels"
|
||||||
|
:value="orgIndex"
|
||||||
|
@change="onOrgChange"
|
||||||
|
>
|
||||||
|
<view class="field-input picker">
|
||||||
|
{{ selectedOrgName || '请选择受理部门' }}
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="field">
|
||||||
|
<text class="field-label">标题<text class="required">*</text></text>
|
||||||
|
<input
|
||||||
|
v-model="form.title"
|
||||||
|
class="field-input"
|
||||||
|
type="text"
|
||||||
|
maxlength="100"
|
||||||
|
placeholder="请输入标题"
|
||||||
|
/>
|
||||||
|
<text class="counter">{{ form.title.length }}/100</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="field">
|
||||||
|
<text class="field-label">内容<text class="required">*</text></text>
|
||||||
|
<textarea
|
||||||
|
v-model="form.content"
|
||||||
|
class="field-textarea"
|
||||||
|
maxlength="2000"
|
||||||
|
placeholder="请输入协调内容"
|
||||||
|
/>
|
||||||
|
<text class="counter">{{ form.content.length }}/2000</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="field">
|
||||||
|
<text class="field-label">备注</text>
|
||||||
|
<textarea
|
||||||
|
v-model="form.remark"
|
||||||
|
class="field-textarea"
|
||||||
|
maxlength="500"
|
||||||
|
placeholder="选填"
|
||||||
|
/>
|
||||||
|
<text class="counter">{{ form.remark.length }}/500</text>
|
||||||
|
</view>
|
||||||
|
</uni-card>
|
||||||
|
|
||||||
|
<uni-card :border="false" :padding="'16px'" title="附件">
|
||||||
|
<view class="category-row">
|
||||||
|
<text class="label">附件分类</text>
|
||||||
|
<view class="chips">
|
||||||
|
<view
|
||||||
|
v-for="item in categories"
|
||||||
|
:key="item.value"
|
||||||
|
class="chip"
|
||||||
|
:class="{ active: category === item.value }"
|
||||||
|
@click="category = item.value"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-for="item in attachments" :key="item.id" class="attachment">
|
||||||
|
<view class="attachment-main">
|
||||||
|
<text class="attachment-name">{{ item.name }}</text>
|
||||||
|
<text class="attachment-meta">
|
||||||
|
{{ item.categoryName }} · {{ formatSize(item.length) }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<text class="delete" @click="removeAttachment(item)">删除</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="attachments.length === 0" class="empty">暂无附件</view>
|
||||||
|
|
||||||
|
<button class="upload-button" :loading="uploading" @click="chooseAttachment">
|
||||||
|
上传附件({{ attachments.length }}/9)
|
||||||
|
</button>
|
||||||
|
<text class="hint">支持 jpg/jpeg/png/pdf/doc/docx/xls/xlsx,单张不超过20MB</text>
|
||||||
|
</uni-card>
|
||||||
|
|
||||||
|
<view class="footer">
|
||||||
|
<button class="footer-button ghost" :loading="saving" @click="saveDraft">
|
||||||
|
保存草稿
|
||||||
|
</button>
|
||||||
|
<button class="footer-button primary" :loading="submitting" @click="submit">
|
||||||
|
提交
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, reactive, ref } from 'vue';
|
||||||
|
import { onLoad } from '@dcloudio/uni-app';
|
||||||
|
import { useUserStore } from '@/store/modules/user';
|
||||||
|
import {
|
||||||
|
getInstance,
|
||||||
|
saveCoordination,
|
||||||
|
submitInstance
|
||||||
|
} from '@/api/approval/instance';
|
||||||
|
import {
|
||||||
|
deleteAttachment,
|
||||||
|
uploadAttachment
|
||||||
|
} from '@/api/approval/attachment';
|
||||||
|
import { getOrganizations } from '@/api/approval/directory';
|
||||||
|
import type {
|
||||||
|
ApprovalAttachment,
|
||||||
|
ApprovalOrganization
|
||||||
|
} from '@/api/approval/model';
|
||||||
|
import { showError, showSuccess } from '@/utils/app';
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const categories = [
|
||||||
|
{ value: 'SIGNED', label: '签字件' },
|
||||||
|
{ value: 'OTHER', label: '其他' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const mode = ref<'create' | 'edit'>('create');
|
||||||
|
const instanceId = ref(0);
|
||||||
|
const organizations = ref<ApprovalOrganization[]>([]);
|
||||||
|
const orgIndex = ref(0);
|
||||||
|
const form = reactive({
|
||||||
|
acceptOrgId: 0,
|
||||||
|
title: '',
|
||||||
|
content: '',
|
||||||
|
remark: ''
|
||||||
|
});
|
||||||
|
const attachments = ref<ApprovalAttachment[]>([]);
|
||||||
|
const category = ref('SIGNED');
|
||||||
|
const saving = ref(false);
|
||||||
|
const submitting = ref(false);
|
||||||
|
const uploading = ref(false);
|
||||||
|
|
||||||
|
const reporterName = computed(
|
||||||
|
() => userStore.info?.realName || userStore.info?.nickname || ''
|
||||||
|
);
|
||||||
|
const reportOrgName = computed(() => userStore.info?.organizationName || '');
|
||||||
|
const organizationLabels = computed(() =>
|
||||||
|
organizations.value.map(item => item.organizationName)
|
||||||
|
);
|
||||||
|
const selectedOrgName = computed(
|
||||||
|
() => organizations.value[orgIndex.value]?.organizationName || ''
|
||||||
|
);
|
||||||
|
|
||||||
|
onLoad(options => {
|
||||||
|
mode.value = options?.mode === 'edit' ? 'edit' : 'create';
|
||||||
|
instanceId.value = Number(options?.id) || 0;
|
||||||
|
loadOrganizations();
|
||||||
|
if (instanceId.value) {
|
||||||
|
loadDetail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function loadOrganizations() {
|
||||||
|
try {
|
||||||
|
organizations.value = await getOrganizations();
|
||||||
|
syncOrgIndex();
|
||||||
|
} catch (error: any) {
|
||||||
|
showError(error?.message || '受理部门加载失败', null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncOrgIndex() {
|
||||||
|
if (!form.acceptOrgId) {
|
||||||
|
orgIndex.value = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const index = organizations.value.findIndex(
|
||||||
|
item => item.organizationId === form.acceptOrgId
|
||||||
|
);
|
||||||
|
orgIndex.value = index >= 0 ? index : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onOrgChange(event: { detail: { value: string | number } }) {
|
||||||
|
orgIndex.value = Number(event.detail.value);
|
||||||
|
const organization = organizations.value[orgIndex.value];
|
||||||
|
form.acceptOrgId = organization ? organization.organizationId : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadDetail() {
|
||||||
|
try {
|
||||||
|
const detail = await getInstance(instanceId.value);
|
||||||
|
if (detail.docType !== 'coordination') {
|
||||||
|
showError('单据类型不匹配', null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!detail.permissions?.canEdit) {
|
||||||
|
showError('当前状态不可修改', null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
form.acceptOrgId = detail.acceptOrgId || 0;
|
||||||
|
form.title = detail.title || '';
|
||||||
|
form.content = detail.content || '';
|
||||||
|
form.remark = detail.remark || '';
|
||||||
|
attachments.value = detail.attachments || [];
|
||||||
|
syncOrgIndex();
|
||||||
|
} catch (error: any) {
|
||||||
|
showError(error?.message || '加载失败', null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate(): string | null {
|
||||||
|
if (!form.acceptOrgId) {
|
||||||
|
return '请选择受理部门';
|
||||||
|
}
|
||||||
|
if (!form.title.trim()) {
|
||||||
|
return '请输入标题';
|
||||||
|
}
|
||||||
|
if (form.title.trim().length > 100) {
|
||||||
|
return '标题不能超过100字';
|
||||||
|
}
|
||||||
|
if (!form.content.trim()) {
|
||||||
|
return '请输入内容';
|
||||||
|
}
|
||||||
|
if (form.content.trim().length > 2000) {
|
||||||
|
return '内容不能超过2000字';
|
||||||
|
}
|
||||||
|
if (form.remark.trim().length > 500) {
|
||||||
|
return '备注不能超过500字';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureDraft(): Promise<number> {
|
||||||
|
const message = validate();
|
||||||
|
if (message) {
|
||||||
|
showError(message, null);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
saving.value = true;
|
||||||
|
try {
|
||||||
|
const id = await saveCoordination({
|
||||||
|
id: instanceId.value || undefined,
|
||||||
|
acceptOrgId: form.acceptOrgId,
|
||||||
|
title: form.title.trim(),
|
||||||
|
content: form.content.trim(),
|
||||||
|
remark: form.remark.trim() || undefined
|
||||||
|
});
|
||||||
|
instanceId.value = id;
|
||||||
|
mode.value = 'edit';
|
||||||
|
return id;
|
||||||
|
} catch (error: any) {
|
||||||
|
showError(error?.message || '保存失败', null);
|
||||||
|
return 0;
|
||||||
|
} finally {
|
||||||
|
saving.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveDraft() {
|
||||||
|
const id = await ensureDraft();
|
||||||
|
if (!id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showSuccess('草稿已保存', null);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submit() {
|
||||||
|
const id = await ensureDraft();
|
||||||
|
if (!id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uni.showModal({
|
||||||
|
title: '提交确认',
|
||||||
|
content: '提交后字段与附件将锁定,确认提交?',
|
||||||
|
success: async result => {
|
||||||
|
if (!result.confirm) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
submitting.value = true;
|
||||||
|
try {
|
||||||
|
const response = await submitInstance(id);
|
||||||
|
showSuccess(response.message || '提交成功', () => {
|
||||||
|
uni.redirectTo({
|
||||||
|
url: '/packageApproval/pages/detail?id=' + id
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
showError(error?.message || '提交失败', null);
|
||||||
|
} finally {
|
||||||
|
submitting.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function chooseAttachment() {
|
||||||
|
if (attachments.value.length >= 9) {
|
||||||
|
showError('每单最多上传9张附件', null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const remaining = 9 - attachments.value.length;
|
||||||
|
// #ifdef H5
|
||||||
|
(uni as any).chooseFile({
|
||||||
|
count: remaining,
|
||||||
|
extension: ['.jpg', '.jpeg', '.png', '.pdf', '.doc', '.docx', '.xls', '.xlsx'],
|
||||||
|
success: (result: any) => uploadFiles(result.tempFiles || []),
|
||||||
|
fail: () => undefined
|
||||||
|
});
|
||||||
|
// #endif
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
(uni as any).chooseMessageFile({
|
||||||
|
count: remaining,
|
||||||
|
type: 'file',
|
||||||
|
success: (result: any) => uploadFiles(result.tempFiles || []),
|
||||||
|
fail: () => undefined
|
||||||
|
});
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
(uni as any).chooseImage({
|
||||||
|
count: remaining,
|
||||||
|
sizeType: ['compressed'],
|
||||||
|
sourceType: ['camera', 'album'],
|
||||||
|
success: (result: any) =>
|
||||||
|
(result.tempFilePaths || []).forEach((path: string) =>
|
||||||
|
uploadFiles([{ path, name: path.split('/').pop() || '附件' }])
|
||||||
|
),
|
||||||
|
fail: () => undefined
|
||||||
|
});
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
||||||
|
async function uploadFiles(files: Array<{ path: string; name?: string; size?: number }>) {
|
||||||
|
if (!files.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const id = await ensureDraft();
|
||||||
|
if (!id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uploading.value = true;
|
||||||
|
try {
|
||||||
|
for (const file of files) {
|
||||||
|
if (file.size && file.size > 20 * 1024 * 1024) {
|
||||||
|
showError('单个附件不能超过20MB', null);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const uploaded = await uploadAttachment(id, file.path, category.value);
|
||||||
|
attachments.value = [...attachments.value, uploaded];
|
||||||
|
}
|
||||||
|
uni.showToast({ title: '上传成功', icon: 'success' });
|
||||||
|
} catch (error: any) {
|
||||||
|
showError(error?.message || '上传失败', null);
|
||||||
|
} finally {
|
||||||
|
uploading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeAttachment(item: ApprovalAttachment) {
|
||||||
|
uni.showModal({
|
||||||
|
title: '删除附件',
|
||||||
|
content: '确认删除该附件?',
|
||||||
|
success: async result => {
|
||||||
|
if (!result.confirm) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await deleteAttachment(item.id);
|
||||||
|
attachments.value = attachments.value.filter(
|
||||||
|
attachment => attachment.id !== item.id
|
||||||
|
);
|
||||||
|
uni.showToast({ title: '已删除', icon: 'success' });
|
||||||
|
} catch (error: any) {
|
||||||
|
showError(error?.message || '删除失败', null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatSize(value?: number) {
|
||||||
|
if (!value) {
|
||||||
|
return '0 B';
|
||||||
|
}
|
||||||
|
if (value < 1024) {
|
||||||
|
return value + ' B';
|
||||||
|
}
|
||||||
|
if (value < 1024 * 1024) {
|
||||||
|
return (value / 1024).toFixed(1) + ' KB';
|
||||||
|
}
|
||||||
|
return (value / 1024 / 1024).toFixed(1) + ' MB';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.form-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f6f8;
|
||||||
|
padding-bottom: 180rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auto-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
width: 160rpx;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
flex: 1;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field {
|
||||||
|
margin-top: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.required {
|
||||||
|
color: #d81e06;
|
||||||
|
margin-left: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-input {
|
||||||
|
height: 72rpx;
|
||||||
|
padding: 0 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
line-height: 72rpx;
|
||||||
|
background: #f7f8fa;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-input.picker {
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 220rpx;
|
||||||
|
padding: 16rpx 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
background: #f7f8fa;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.counter {
|
||||||
|
display: block;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chips {
|
||||||
|
display: flex;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip {
|
||||||
|
padding: 8rpx 24rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #606266;
|
||||||
|
background: #f2f3f5;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip.active {
|
||||||
|
color: #ffffff;
|
||||||
|
background: #d81e06;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 16rpx 0;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-name {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #303133;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-meta {
|
||||||
|
margin-top: 6rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete {
|
||||||
|
margin-left: 16rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #d81e06;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
padding: 24rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-button {
|
||||||
|
margin-top: 16rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
background: #f7f8fa;
|
||||||
|
color: #303133;
|
||||||
|
border: 1rpx dashed #dcdfe6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint {
|
||||||
|
display: block;
|
||||||
|
margin-top: 12rpx;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
gap: 24rpx;
|
||||||
|
padding: 20rpx 32rpx calc(20rpx + env(safe-area-inset-bottom));
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-button {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 30rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-button.primary {
|
||||||
|
background: #d81e06;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-button.ghost {
|
||||||
|
background: #ffffff;
|
||||||
|
color: #303133;
|
||||||
|
border: 1rpx solid #dcdfe6;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -66,8 +66,13 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="activeTab === 'mine'" class="fab" @click="createDraft">
|
<view v-if="activeTab === 'mine' && canSubmit" class="create-bar">
|
||||||
<text class="fab-text">+</text>
|
<button class="create-button" @click="createCompletion">
|
||||||
|
新建确认单
|
||||||
|
</button>
|
||||||
|
<button class="create-button primary" @click="createCoordination">
|
||||||
|
新建协调单
|
||||||
|
</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -82,6 +87,7 @@ import {
|
|||||||
} from '@dcloudio/uni-app';
|
} from '@dcloudio/uni-app';
|
||||||
import { pageDone, pageMine, pageTodo } from '@/api/approval/instance';
|
import { pageDone, pageMine, pageTodo } from '@/api/approval/instance';
|
||||||
import type { ApprovalItem, ApprovalTaskListParam } from '@/api/approval/model';
|
import type { ApprovalItem, ApprovalTaskListParam } from '@/api/approval/model';
|
||||||
|
import { hasPermission } from '@/utils/permission';
|
||||||
|
|
||||||
type TabKey = 'todo' | 'mine' | 'done';
|
type TabKey = 'todo' | 'mine' | 'done';
|
||||||
|
|
||||||
@@ -103,6 +109,7 @@ const statusOptions = [
|
|||||||
const statusLabels = statusOptions.map(option => option.label);
|
const statusLabels = statusOptions.map(option => option.label);
|
||||||
|
|
||||||
const activeTab = ref<TabKey>('todo');
|
const activeTab = ref<TabKey>('todo');
|
||||||
|
const canSubmit = computed(() => hasPermission('approval:instance:submit'));
|
||||||
const emptyText = computed(() => {
|
const emptyText = computed(() => {
|
||||||
if (activeTab.value === 'todo') {
|
if (activeTab.value === 'todo') {
|
||||||
return '暂无待办';
|
return '暂无待办';
|
||||||
@@ -207,12 +214,18 @@ function openDetail(id: number) {
|
|||||||
uni.navigateTo({ url: '/packageApproval/pages/detail?id=' + id });
|
uni.navigateTo({ url: '/packageApproval/pages/detail?id=' + id });
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDraft() {
|
function createCompletion() {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/packageApproval/pages/form-completion?mode=create'
|
url: '/packageApproval/pages/form-completion?mode=create'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createCoordination() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/packageApproval/pages/form-coordination?mode=create'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function formatTime(value?: string) {
|
function formatTime(value?: string) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return '—';
|
return '—';
|
||||||
@@ -368,23 +381,30 @@ function formatTime(value?: string) {
|
|||||||
color: #909399;
|
color: #909399;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fab {
|
.create-bar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 40rpx;
|
left: 0;
|
||||||
bottom: 80rpx;
|
right: 0;
|
||||||
width: 100rpx;
|
bottom: 0;
|
||||||
height: 100rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: #d81e06;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
gap: 24rpx;
|
||||||
justify-content: center;
|
padding: 20rpx 32rpx calc(20rpx + env(safe-area-inset-bottom));
|
||||||
box-shadow: 0 8rpx 20rpx rgba(216, 30, 6, 0.35);
|
background: #ffffff;
|
||||||
|
box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
.fab-text {
|
.create-button {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 28rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #303133;
|
||||||
|
border: 1rpx solid #dcdfe6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-button.primary {
|
||||||
|
background: #d81e06;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-size: 56rpx;
|
border-color: #d81e06;
|
||||||
line-height: 1;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -56,6 +56,13 @@
|
|||||||
"navigationBarTitleText": "业务完成确认单",
|
"navigationBarTitleText": "业务完成确认单",
|
||||||
"enablePullDownRefresh": false
|
"enablePullDownRefresh": false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/form-coordination",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "内部协调单",
|
||||||
|
"enablePullDownRefresh": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user