公文流转
This commit is contained in:
parent
9e2d89e911
commit
7f3c9705cc
@ -3,166 +3,115 @@ import { get, post } from "@/utils/request";
|
|||||||
// 公文相关API接口
|
// 公文相关API接口
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取公文列表
|
* 分页查询公文列表
|
||||||
* @param params 查询参数
|
|
||||||
*/
|
*/
|
||||||
export function getGwListApi(params: {
|
export function gwFindPageApi(params: any) {
|
||||||
page: number;
|
return get('/api/gw/findPage', params);
|
||||||
pageSize: number;
|
|
||||||
status?: string;
|
|
||||||
keyword?: string;
|
|
||||||
gwType?: string;
|
|
||||||
urgencyLevel?: string;
|
|
||||||
startTime?: string;
|
|
||||||
endTime?: string;
|
|
||||||
}) {
|
|
||||||
return get('/api/gw/list', params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取公文详情
|
* 根据ID查询公文详情
|
||||||
* @param id 公文ID
|
|
||||||
*/
|
*/
|
||||||
export function getGwDetailApi(id: string) {
|
export function gwFindByIdApi(params: any) {
|
||||||
return get(`/api/gw/detail/${id}`);
|
return get('/api/gw/findById', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建公文
|
* 新增/修改公文
|
||||||
* @param data 公文数据
|
|
||||||
*/
|
*/
|
||||||
export function createGwApi(data: {
|
export function gwSaveApi(params: any) {
|
||||||
title: string;
|
return post('/api/gw/save', params);
|
||||||
gwType: string;
|
|
||||||
urgencyLevel: string;
|
|
||||||
remark?: string;
|
|
||||||
files: any[];
|
|
||||||
approvers: any[];
|
|
||||||
ccUsers: any[];
|
|
||||||
}) {
|
|
||||||
return post('/api/gw/create', data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新公文
|
* 软删除公文
|
||||||
* @param id 公文ID
|
|
||||||
* @param data 更新数据
|
|
||||||
*/
|
*/
|
||||||
export function updateGwApi(id: string, data: any) {
|
export function gwLogicDeleteApi(params: any) {
|
||||||
return post(`/api/gw/update/${id}`, data);
|
// 将参数拼接到URL中,确保后端能正确接收
|
||||||
}
|
const queryString = Object.keys(params)
|
||||||
|
.map(key => `${key}=${encodeURIComponent(params[key])}`)
|
||||||
|
.join('&');
|
||||||
|
|
||||||
/**
|
// 尝试POST请求,如果不行可以改为GET请求
|
||||||
* 删除公文
|
return post(`/api/gw/logicDelete?${queryString}`, params);
|
||||||
* @param id 公文ID
|
|
||||||
*/
|
// 如果POST请求有问题,可以尝试GET请求:
|
||||||
export function deleteGwApi(id: string) {
|
// return get(`/api/gw/logicDelete?${queryString}`);
|
||||||
return post(`/api/gw/delete/${id}`, { id });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存草稿
|
* 保存草稿
|
||||||
* @param data 草稿数据
|
|
||||||
*/
|
*/
|
||||||
export function saveDraftApi(data: any) {
|
export function gwDraftApi(params: any) {
|
||||||
return post('/api/gw/draft', data);
|
return post('/api/gw/draft', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交公文
|
* 提交公文
|
||||||
* @param data 提交数据
|
|
||||||
*/
|
*/
|
||||||
export function submitGwApi(data: any) {
|
export function gwSubmitApi(params: any) {
|
||||||
return post('/api/gw/submit', data);
|
return post('/api/gw/submit', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存变更
|
* 审批公文
|
||||||
* @param data 变更数据
|
|
||||||
*/
|
*/
|
||||||
export function saveChangesApi(data: {
|
export function gwApproveApi(params: any) {
|
||||||
gwId: string;
|
return post('/api/gw/approve', params);
|
||||||
approvers: any[];
|
|
||||||
ccUsers: any[];
|
|
||||||
operationLogs: any[];
|
|
||||||
}) {
|
|
||||||
return post('/api/gw/changes', data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 搜索用户
|
* 抄送确认
|
||||||
* @param keyword 搜索关键词
|
|
||||||
*/
|
*/
|
||||||
export function searchUsersApi(keyword: string) {
|
export function gwCcConfirmApi(params: any) {
|
||||||
return get('/api/user/search', { keyword });
|
return post('/api/gw/cc-confirm', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取审批人列表
|
* 获取审批人列表
|
||||||
* @param gwId 公文ID
|
|
||||||
*/
|
*/
|
||||||
export function getApproversApi(gwId: string) {
|
export function gwApproversApi(params: any) {
|
||||||
return get(`/api/gw/approvers/${gwId}`);
|
return get('/api/gw/approvers', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取抄送人列表
|
* 获取抄送人列表
|
||||||
* @param gwId 公文ID
|
|
||||||
*/
|
*/
|
||||||
export function getCCUsersApi(gwId: string) {
|
export function gwCcUsersApi(params: any) {
|
||||||
return get(`/api/gw/cc-users/${gwId}`);
|
return get('/api/gw/cc-users', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取操作记录
|
* 获取操作记录
|
||||||
* @param gwId 公文ID
|
|
||||||
*/
|
*/
|
||||||
export function getOperationLogsApi(gwId: string) {
|
export function gwLogsApi(params: any) {
|
||||||
return get(`/api/gw/logs/${gwId}`);
|
return get('/api/gw/logs', params);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 文件上传
|
|
||||||
* @param file 文件对象
|
|
||||||
*/
|
|
||||||
export function uploadFileApi(file: File) {
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append("file", file);
|
|
||||||
|
|
||||||
return post('/api/file/upload', formData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 文件删除
|
|
||||||
* @param fileId 文件ID
|
|
||||||
*/
|
|
||||||
export function deleteFileApi(fileId: string) {
|
|
||||||
return post(`/api/file/delete/${fileId}`, { fileId });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取公文统计信息
|
* 获取公文统计信息
|
||||||
*/
|
*/
|
||||||
export function getGwStatsApi() {
|
export function gwStatsApi(params: any) {
|
||||||
return get('/api/gw/stats');
|
return get('/api/gw/stats', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 审批公文
|
* 文件上传
|
||||||
* @param data 审批数据
|
|
||||||
*/
|
*/
|
||||||
export function approveGwApi(data: {
|
export function fileUploadApi(params: any) {
|
||||||
gwId: string;
|
return post('/api/file/upload', params);
|
||||||
action: "approve" | "reject";
|
|
||||||
remark?: string;
|
|
||||||
}) {
|
|
||||||
return post('/api/gw/approve', data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 抄送确认
|
* 文件删除
|
||||||
* @param gwId 公文ID
|
|
||||||
*/
|
*/
|
||||||
export function confirmCCApi(gwId: string) {
|
export function fileDeleteApi(params: any) {
|
||||||
return post(`/api/gw/cc-confirm/${gwId}`);
|
return post('/api/file/delete', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索用户
|
||||||
|
*/
|
||||||
|
export function userSearchApi(params: any) {
|
||||||
|
return get('/api/user/search', params);
|
||||||
}
|
}
|
||||||
|
|||||||
112
src/api/routine/lcglSetApi.ts
Normal file
112
src/api/routine/lcglSetApi.ts
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import { get, post } from "@/utils/request";
|
||||||
|
|
||||||
|
// 流程管理设置相关API接口
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询流程设置
|
||||||
|
* @param params 查询参数
|
||||||
|
*/
|
||||||
|
export function lcglSetFindPageApi(params: {
|
||||||
|
page: number;
|
||||||
|
pageSize: number;
|
||||||
|
ruleId?: string;
|
||||||
|
ruleName?: string;
|
||||||
|
status?: string;
|
||||||
|
startTime?: string;
|
||||||
|
endTime?: string;
|
||||||
|
}) {
|
||||||
|
return get('/api/lcglSet/findPage', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增/修改流程设置
|
||||||
|
* @param params 流程设置数据
|
||||||
|
*/
|
||||||
|
export function lcglSetSaveApi(params: {
|
||||||
|
id?: string;
|
||||||
|
ruleId: string;
|
||||||
|
ruleName: string;
|
||||||
|
spId?: string;
|
||||||
|
ccId?: string;
|
||||||
|
status?: string;
|
||||||
|
remark?: string;
|
||||||
|
}) {
|
||||||
|
return post('/api/lcglSet/save', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 逻辑删除流程设置
|
||||||
|
* @param params 删除参数
|
||||||
|
*/
|
||||||
|
export function lcglSetLogicDeleteApi(params: { id: string }) {
|
||||||
|
return post('/api/lcglSet/logicDelete', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询流程设置详情
|
||||||
|
* @param params 查询参数
|
||||||
|
*/
|
||||||
|
export function lcglSetFindByIdApi(params: { id: string }) {
|
||||||
|
return get('/api/lcglSet/findById', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有流程设置
|
||||||
|
*/
|
||||||
|
export function lcglSetFindAllApi() {
|
||||||
|
return get('/api/lcglSet/findAll');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据规则ID查询流程设置
|
||||||
|
* @param params 查询参数
|
||||||
|
*/
|
||||||
|
export function lcglSetFindByRuleIdApi(params: { ruleId: string }) {
|
||||||
|
return get('/api/lcglSet/findByRuleId', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新流程设置状态
|
||||||
|
* @param params 更新参数
|
||||||
|
*/
|
||||||
|
export function lcglSetUpdateStatusApi(params: {
|
||||||
|
id: string;
|
||||||
|
status: string;
|
||||||
|
}) {
|
||||||
|
return post('/api/lcglSet/updateStatus', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制流程设置
|
||||||
|
* @param params 复制参数
|
||||||
|
*/
|
||||||
|
export function lcglSetCopyApi(params: {
|
||||||
|
id: string;
|
||||||
|
newRuleId: string;
|
||||||
|
newRuleName: string;
|
||||||
|
}) {
|
||||||
|
return post('/api/lcglSet/copy', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出流程设置
|
||||||
|
* @param params 导出参数
|
||||||
|
*/
|
||||||
|
export function lcglSetExportApi(params: {
|
||||||
|
ids?: string[];
|
||||||
|
ruleId?: string;
|
||||||
|
status?: string;
|
||||||
|
}) {
|
||||||
|
return post('/api/lcglSet/export', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入流程设置
|
||||||
|
* @param params 导入参数
|
||||||
|
*/
|
||||||
|
export function lcglSetImportApi(params: {
|
||||||
|
file: File;
|
||||||
|
overwrite?: boolean;
|
||||||
|
}) {
|
||||||
|
return post('/api/lcglSet/import', params);
|
||||||
|
}
|
||||||
91
src/components/BasicForm/components/BasicJsPicker.vue
Normal file
91
src/components/BasicForm/components/BasicJsPicker.vue
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<template>
|
||||||
|
<view class="py-7">
|
||||||
|
<BasicJsPicker
|
||||||
|
ref="pickerRef"
|
||||||
|
v-model="pickerValue"
|
||||||
|
v-bind="attrs.componentProps"
|
||||||
|
@change="handleChange"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
/**
|
||||||
|
* FormBasicJsPicker 教师选择器表单组件
|
||||||
|
* 用于在 BasicForm 中使用 BasicJsPicker 组件
|
||||||
|
*/
|
||||||
|
import BasicJsPicker from "@/components/BasicJsPicker/Picker.vue";
|
||||||
|
|
||||||
|
const pickerRef = ref<any>(null);
|
||||||
|
|
||||||
|
const props = defineProps(["modelValue"]);
|
||||||
|
const emit = defineEmits(["update:modelValue"]);
|
||||||
|
const attrs: any = useAttrs();
|
||||||
|
|
||||||
|
// 计算显示值
|
||||||
|
const pickerValue = computed({
|
||||||
|
get() {
|
||||||
|
return props.modelValue;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
emit("update:modelValue", value);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 注意:displayValue 和 open 函数已移除,因为 BasicJsPicker 组件本身已经处理了显示和交互
|
||||||
|
|
||||||
|
// 处理选择变化
|
||||||
|
const handleChange = (value: any) => {
|
||||||
|
// 根据 multiple 属性处理返回值
|
||||||
|
if (attrs.componentProps.multiple) {
|
||||||
|
// 多选模式:返回数组
|
||||||
|
pickerValue.value = value;
|
||||||
|
} else {
|
||||||
|
// 单选模式:返回单个值或 null
|
||||||
|
pickerValue.value = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 暴露方法给父组件
|
||||||
|
defineExpose({
|
||||||
|
open: () => pickerRef.value?.open(),
|
||||||
|
close: () => pickerRef.value?.close()
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.py-7 {
|
||||||
|
padding: 7rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wh-full {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-center {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-between {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-13 {
|
||||||
|
font-size: 13rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-ellipsis-1 {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-9 {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -8,6 +8,7 @@
|
|||||||
<FormBasicCode v-bind="attrs" v-if="isShow('BasicCode')" v-model="newValue"/>
|
<FormBasicCode v-bind="attrs" v-if="isShow('BasicCode')" v-model="newValue"/>
|
||||||
<FormBasicSwitch v-bind="attrs" v-if="isShow('BasicSwitch')" v-model="newValue"/>
|
<FormBasicSwitch v-bind="attrs" v-if="isShow('BasicSwitch')" v-model="newValue"/>
|
||||||
<FormBasicPicker v-bind="attrs" v-if="isShow('BasicPicker')" v-model="newValue"/>
|
<FormBasicPicker v-bind="attrs" v-if="isShow('BasicPicker')" v-model="newValue"/>
|
||||||
|
<FormBasicJsPicker v-bind="attrs" v-if="isShow('BasicJsPicker')" v-model="newValue"/>
|
||||||
<FormBasicNumberBox v-bind="attrs" v-if="isShow('BasicNumberBox')" v-model="newValue"/>
|
<FormBasicNumberBox v-bind="attrs" v-if="isShow('BasicNumberBox')" v-model="newValue"/>
|
||||||
<FormBasicSelectBox v-bind="attrs" v-if="isShow('BasicSelectBox')" v-model="newValue"/>
|
<FormBasicSelectBox v-bind="attrs" v-if="isShow('BasicSelectBox')" v-model="newValue"/>
|
||||||
<FormBasicLocation v-bind="attrs" v-if="isShow('BasicLocation')" v-model="newValue"/>
|
<FormBasicLocation v-bind="attrs" v-if="isShow('BasicLocation')" v-model="newValue"/>
|
||||||
|
|||||||
1
src/components/BasicForm/type/useForm.d.ts
vendored
1
src/components/BasicForm/type/useForm.d.ts
vendored
@ -17,6 +17,7 @@ type Component =
|
|||||||
| 'BasicDateTime'
|
| 'BasicDateTime'
|
||||||
| 'BasicInput'
|
| 'BasicInput'
|
||||||
| 'BasicPicker'
|
| 'BasicPicker'
|
||||||
|
| 'BasicJsPicker'
|
||||||
| 'BasicRate'
|
| 'BasicRate'
|
||||||
| 'BasicSwitch'
|
| 'BasicSwitch'
|
||||||
| 'BasicUpload'
|
| 'BasicUpload'
|
||||||
|
|||||||
387
src/components/BasicJsPicker/Picker.vue
Normal file
387
src/components/BasicJsPicker/Picker.vue
Normal file
@ -0,0 +1,387 @@
|
|||||||
|
<template>
|
||||||
|
<view class="basic-js-picker">
|
||||||
|
<view class="js-selected" @click="showPicker">
|
||||||
|
<view class="js-selected-name">
|
||||||
|
<text class="data" v-if="selectedList && selectedList.length">{{ getShowSelectedName() }}</text>
|
||||||
|
<text class="data" style="color: #999;" v-else>{{ placeholder }}</text>
|
||||||
|
</view>
|
||||||
|
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
||||||
|
</view>
|
||||||
|
<u-popup :show="showPopup" @close="showPopup=false">
|
||||||
|
<view class="js-picker-popup">
|
||||||
|
<view class="js-picker-header">
|
||||||
|
<view class="js-cancel-btn" @click="handleCancel">取消</view>
|
||||||
|
<view class="js-picker-title">{{ title }}</view>
|
||||||
|
<view class="js-ok-btn" @click="handleOk">确定</view>
|
||||||
|
</view>
|
||||||
|
<view class="js-picker-search" v-if="showSearch">
|
||||||
|
<BasicSearch @change="handleSearch" :showAction="false" height="36" :placeholder="searchPlaceholder"/>
|
||||||
|
</view>
|
||||||
|
<view class="js-list">
|
||||||
|
<scroll-view scroll-y style="max-height: 60vh" :scroll-top="targetScrollTop">
|
||||||
|
<view class="js-item" v-for="(item, index) in jsList" :key="index"
|
||||||
|
:class="{ selected: item.selected }" @click="handleSelect(item)">
|
||||||
|
<view class="js-name">{{ item.label }}</view>
|
||||||
|
<BasicIcon type="checkmarkempty" v-if="item.selected" color="#333" />
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</u-popup>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useCommonStore } from "@/store/modules/common";
|
||||||
|
const { getAllJs } = useCommonStore();
|
||||||
|
|
||||||
|
// 接收外部传入属性
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
modelValue?: any,
|
||||||
|
defaultValue?: any,
|
||||||
|
parentData?: any,
|
||||||
|
multiple?: boolean,
|
||||||
|
// 排除id列表
|
||||||
|
excludeIds?: any,
|
||||||
|
// 自定义配置
|
||||||
|
title?: string,
|
||||||
|
placeholder?: string,
|
||||||
|
searchPlaceholder?: string,
|
||||||
|
showSearch?: boolean
|
||||||
|
}>(), {
|
||||||
|
modelValue: null,
|
||||||
|
defaultValue: null,
|
||||||
|
parentData: null,
|
||||||
|
multiple: false,
|
||||||
|
excludeIds: [],
|
||||||
|
title: '请选择教师',
|
||||||
|
placeholder: '请选择老师',
|
||||||
|
searchPlaceholder: '输入教师名称查询',
|
||||||
|
showSearch: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// 定义一个上级传入的emit响应事件用于接收数据变更
|
||||||
|
const emit = defineEmits(['change', 'update:modelValue'])
|
||||||
|
|
||||||
|
const targetScrollTop = ref(0); // 用于绑定 scroll-top
|
||||||
|
const showPopup = ref(false);
|
||||||
|
|
||||||
|
const jsListAll = ref<any>([]);
|
||||||
|
const jsList = ref<any>([]);
|
||||||
|
let searchKey = "";
|
||||||
|
|
||||||
|
const selectedList = ref<any>([]);
|
||||||
|
|
||||||
|
// 计算属性,用于 v-model 双向绑定
|
||||||
|
const currentValue = computed({
|
||||||
|
get() {
|
||||||
|
return props.modelValue;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
emit('update:modelValue', value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加一个内部状态来跟踪当前值
|
||||||
|
const internalValue = ref(props.modelValue || props.defaultValue);
|
||||||
|
|
||||||
|
const getShowSelectedName = () => {
|
||||||
|
const names = selectedList.value.map((item: any) => item.label).join(",");
|
||||||
|
return names;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = (value: any) => {
|
||||||
|
searchKey = value;
|
||||||
|
rebuildJsList();
|
||||||
|
return
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSelect = (item: any) => {
|
||||||
|
if (props.multiple) {
|
||||||
|
// 多选模式:切换选中状态
|
||||||
|
const index = selectedList.value.findIndex((selected: any) => selected.value === item.value);
|
||||||
|
if (index > -1) {
|
||||||
|
// 取消选择:从选中列表中移除
|
||||||
|
selectedList.value.splice(index, 1);
|
||||||
|
item.selected = false;
|
||||||
|
} else {
|
||||||
|
// 新增选择:添加到选中列表末尾(保持选择顺序)
|
||||||
|
selectedList.value.push(item);
|
||||||
|
item.selected = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 单选模式:清除其他选中状态,设置当前选中
|
||||||
|
jsList.value.forEach((listItem: any) => listItem.selected = false);
|
||||||
|
selectedList.value = [item];
|
||||||
|
item.selected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
showPopup.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOk = () => {
|
||||||
|
showPopup.value = false;
|
||||||
|
let result;
|
||||||
|
if (props.multiple) {
|
||||||
|
result = selectedList.value;
|
||||||
|
} else {
|
||||||
|
result = selectedList.value[0] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 v-model 值
|
||||||
|
currentValue.value = result;
|
||||||
|
|
||||||
|
// 触发 change 事件
|
||||||
|
emit("change", result, props.parentData);
|
||||||
|
};
|
||||||
|
|
||||||
|
const showPicker = () => {
|
||||||
|
showPopup.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const rebuildJsList = () => {
|
||||||
|
jsList.value = [];
|
||||||
|
jsListAll.value.forEach((item: any) => {
|
||||||
|
if (props.excludeIds && props.excludeIds.length && props.excludeIds.includes(item.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 判断教师姓名jsxm包含搜索关键词
|
||||||
|
if (!searchKey || item.jsxm.includes(searchKey)) {
|
||||||
|
jsList.value.push({
|
||||||
|
label: item.jsxm,
|
||||||
|
value: item.id,
|
||||||
|
headPic: item.headPic,
|
||||||
|
selected: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 恢复选中状态
|
||||||
|
restoreSelection();
|
||||||
|
};
|
||||||
|
|
||||||
|
const restoreSelection = () => {
|
||||||
|
if (!selectedList.value.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按照selectedList的顺序恢复选中状态,保持选择顺序
|
||||||
|
jsList.value.forEach((item: any) => {
|
||||||
|
const isSelected = selectedList.value.some((selected: any) => selected.value === item.value);
|
||||||
|
item.selected = isSelected;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 暴露方法给父组件
|
||||||
|
defineExpose({
|
||||||
|
showPicker,
|
||||||
|
open: showPicker,
|
||||||
|
close: () => showPopup.value = false
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const res = await getAllJs()
|
||||||
|
jsListAll.value = res.result || [];
|
||||||
|
|
||||||
|
rebuildJsList();
|
||||||
|
|
||||||
|
// 等待下一个tick确保jsList已经构建完成
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
// 设置默认值
|
||||||
|
const initialValue = internalValue.value || props.defaultValue;
|
||||||
|
|
||||||
|
if (initialValue) {
|
||||||
|
setInitialValue(initialValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 设置初始值的函数
|
||||||
|
const setInitialValue = (value: any) => {
|
||||||
|
if (!jsList.value.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.multiple) {
|
||||||
|
if (!Array.isArray(value) || !value.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedList.value = [];
|
||||||
|
|
||||||
|
// 先清空所有选中状态
|
||||||
|
jsList.value.forEach((item: any) => {
|
||||||
|
item.selected = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理不同类型的输入值
|
||||||
|
let processedValue = value;
|
||||||
|
|
||||||
|
// 如果输入的是对象数组(如{0: {value: '1001', label: '张三'}, 1: {value: '1002', label: '李四'}})
|
||||||
|
if (value[0] && typeof value[0] === 'object' && (value[0].value || value[0].id)) {
|
||||||
|
processedValue = value.map((item: any) => item.value || item.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按照传入值的顺序设置选中状态,保持选择顺序
|
||||||
|
selectedList.value = [];
|
||||||
|
|
||||||
|
// 遍历传入的值,按照顺序查找对应的项目
|
||||||
|
processedValue.forEach((id: any) => {
|
||||||
|
const foundItem = jsList.value.find((item: any) => item.value === id || item.id === id);
|
||||||
|
if (foundItem) {
|
||||||
|
foundItem.selected = true;
|
||||||
|
selectedList.value.push(foundItem);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 清空其他未选中的项目
|
||||||
|
jsList.value.forEach((item: any) => {
|
||||||
|
if (!processedValue.includes(item.value) && !processedValue.includes(item.id)) {
|
||||||
|
item.selected = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 单选模式
|
||||||
|
|
||||||
|
// 清空所有选中状态
|
||||||
|
jsList.value.forEach((item: any) => {
|
||||||
|
item.selected = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理不同类型的输入值
|
||||||
|
let processedValue = value;
|
||||||
|
if (value && typeof value === 'object' && (value.value || value.id)) {
|
||||||
|
processedValue = value.value || value.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查value是否匹配当前项的value或id
|
||||||
|
const targetItem = jsList.value.find((item: any) => item.value === processedValue || item.id === processedValue);
|
||||||
|
if (targetItem) {
|
||||||
|
targetItem.selected = true;
|
||||||
|
selectedList.value = [targetItem];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果selectedList仍然为空,尝试从jsListAll中查找
|
||||||
|
if (selectedList.value.length === 0 && value && Array.isArray(value)) {
|
||||||
|
selectedList.value = [];
|
||||||
|
|
||||||
|
// 处理不同类型的输入值
|
||||||
|
let processedValue = value;
|
||||||
|
if (value[0] && typeof value[0] === 'object' && (value[0].value || value[0].id)) {
|
||||||
|
processedValue = value.map((item: any) => item.value || item.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
processedValue.forEach((id: any) => {
|
||||||
|
const foundItem = jsListAll.value.find((item: any) => item.id === id);
|
||||||
|
if (foundItem) {
|
||||||
|
const itemForList = {
|
||||||
|
label: foundItem.jsxm,
|
||||||
|
value: foundItem.id,
|
||||||
|
headPic: foundItem.headPic,
|
||||||
|
selected: true
|
||||||
|
};
|
||||||
|
selectedList.value.push(itemForList);
|
||||||
|
|
||||||
|
// 同时更新jsList中对应项的选中状态
|
||||||
|
const jsListItem = jsList.value.find((item: any) => item.value === foundItem.id);
|
||||||
|
if (jsListItem) {
|
||||||
|
jsListItem.selected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听 modelValue 变化,同步到内部状态
|
||||||
|
watch(() => props.modelValue, (newValue) => {
|
||||||
|
if (newValue !== internalValue.value) {
|
||||||
|
internalValue.value = newValue;
|
||||||
|
// 立即尝试设置值,如果jsList还没准备好,会在jsList变化时再次尝试
|
||||||
|
if (jsList.value.length > 0) {
|
||||||
|
setInitialValue(newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
|
// 监听 defaultValue 变化
|
||||||
|
watch(() => props.defaultValue, (newValue) => {
|
||||||
|
if (newValue && jsList.value.length > 0) {
|
||||||
|
setInitialValue(newValue);
|
||||||
|
}
|
||||||
|
}, { immediate: false });
|
||||||
|
|
||||||
|
// 监听 jsList 变化,当jsList构建完成后设置初始值
|
||||||
|
watch(() => jsList.value, (newJsList) => {
|
||||||
|
if (newJsList.length > 0) {
|
||||||
|
const initialValue = internalValue.value || props.defaultValue;
|
||||||
|
|
||||||
|
if (initialValue) {
|
||||||
|
setInitialValue(initialValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, { immediate: false });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.basic-js-picker {
|
||||||
|
flex: 1;
|
||||||
|
.js-selected {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
.js-selected-name {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.js-picker-popup {
|
||||||
|
min-height: 50vh;
|
||||||
|
max-height: 90vh;
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.js-picker-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-weight: bold;
|
||||||
|
.js-cancel-btn {
|
||||||
|
flex: 0 0 50;
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
.js-ok-btn {
|
||||||
|
flex: 0 0 50;
|
||||||
|
padding: 5px 10px;
|
||||||
|
color: #3c9cff;
|
||||||
|
}
|
||||||
|
.js-picker-title {
|
||||||
|
flex: 1 0 1px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.js-picker-search {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
.js-list {
|
||||||
|
flex: 1 0 1px;
|
||||||
|
.js-item {
|
||||||
|
display: flex;
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
&.selected {
|
||||||
|
background-color: beige;
|
||||||
|
}
|
||||||
|
.js-name {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -35,7 +35,14 @@ function deletePic(event: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps(['modelValue'])
|
// 添加 beforeUpload prop
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: String,
|
||||||
|
beforeUpload: {
|
||||||
|
type: Function,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (props.modelValue) {
|
if (props.modelValue) {
|
||||||
@ -67,6 +74,12 @@ async function afterRead(event: any) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
for (let i = 0; i < lists.length; i++) {
|
for (let i = 0; i < lists.length; i++) {
|
||||||
|
if (props.beforeUpload && !props.beforeUpload(lists[i])) {
|
||||||
|
// 如果 beforeUpload 返回 false,移除该文件并跳过上传
|
||||||
|
data.fileList.splice(fileListLen + i, 1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
showLoading({title: '上传中'})
|
showLoading({title: '上传中'})
|
||||||
const res:any = await attachmentUpload(lists[i].url)
|
const res:any = await attachmentUpload(lists[i].url)
|
||||||
const result = res.result
|
const result = res.result
|
||||||
|
|||||||
@ -179,14 +179,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/view/routine/GongWenLiuZhuan/index",
|
"path": "pages/view/routine/gwlz/gwDetail",
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "公文流转",
|
|
||||||
"enablePullDownRefresh": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "pages/view/routine/GongWenLiuZhuan/detail",
|
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "公文详情",
|
"navigationBarTitleText": "公文详情",
|
||||||
"enablePullDownRefresh": false
|
"enablePullDownRefresh": false
|
||||||
@ -195,21 +188,21 @@
|
|||||||
{
|
{
|
||||||
"path": "pages/view/routine/gwlz/gwAdd",
|
"path": "pages/view/routine/gwlz/gwAdd",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "公文列表",
|
"navigationBarTitleText": "公文调整",
|
||||||
"enablePullDownRefresh": false
|
"enablePullDownRefresh": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/view/routine/gwlz/index",
|
"path": "pages/view/routine/gwlz/index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "公文接收",
|
"navigationBarTitleText": "公文列表",
|
||||||
"enablePullDownRefresh": false
|
"enablePullDownRefresh": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/view/routine/gwlz/gwFlow",
|
"path": "pages/view/routine/gwlz/gwFlow",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "公文流转",
|
"navigationBarTitleText": "公文审批",
|
||||||
"enablePullDownRefresh": false
|
"enablePullDownRefresh": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -344,6 +344,7 @@ const sections = reactive<Section[]>([
|
|||||||
permissionKey: "routine-gwlz", // 公文流转权限编码
|
permissionKey: "routine-gwlz", // 公文流转权限编码
|
||||||
path: "/pages/view/routine/gwlz/index",
|
path: "/pages/view/routine/gwlz/index",
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: "hr3",
|
id: "hr3",
|
||||||
icon: "gz",
|
icon: "gz",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
210
src/pages/view/routine/gwlz/gwDetail.vue
Normal file
210
src/pages/view/routine/gwlz/gwDetail.vue
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
<template>
|
||||||
|
<BasicLayout>
|
||||||
|
<view class="px-15 pb-15">
|
||||||
|
<BasicForm @register="register" :readonly="true"> </BasicForm>
|
||||||
|
</view>
|
||||||
|
</BasicLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { navigateTo } from "@/utils/uniapp";
|
||||||
|
import { useForm } from "@/components/BasicForm/hooks/useForm";
|
||||||
|
import { findDicTreeByPidApi } from "@/api/system/dic";
|
||||||
|
import { gwFindByIdApi } from "@/api/routine/gw";
|
||||||
|
import { useDicStore } from "@/store/modules/dic";
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { onLoad } from "@dcloudio/uni-app";
|
||||||
|
|
||||||
|
const { findByPid } = useDicStore();
|
||||||
|
|
||||||
|
// 公文ID
|
||||||
|
const gwId = ref<string>('');
|
||||||
|
|
||||||
|
// 表单配置
|
||||||
|
const [register, { setValue, setSchema }] = useForm({
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
title: "公文信息",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "title",
|
||||||
|
label: "公文标题",
|
||||||
|
component: "BasicInput",
|
||||||
|
componentProps: {
|
||||||
|
placeholder: "公文标题",
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "docType",
|
||||||
|
label: "公文类型",
|
||||||
|
component: "BasicPicker",
|
||||||
|
componentProps: {
|
||||||
|
api: findByPid,
|
||||||
|
param: { pid: "2146632731" }, // 公文类型字典
|
||||||
|
rangeKey: "dictionaryValue",
|
||||||
|
savaKey: "dictionaryCode",
|
||||||
|
placeholder: "公文类型",
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "urgencyLevel",
|
||||||
|
label: "紧急程度",
|
||||||
|
component: "BasicPicker",
|
||||||
|
componentProps: {
|
||||||
|
api: findByPid,
|
||||||
|
param: { pid: "2146632735" }, // 紧急程度字典
|
||||||
|
rangeKey: "dictionaryValue",
|
||||||
|
savaKey: "dictionaryCode",
|
||||||
|
placeholder: "紧急程度",
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "fileUrl",
|
||||||
|
label: "附件",
|
||||||
|
component: "BasicUpload",
|
||||||
|
componentProps: {
|
||||||
|
multiple: false,
|
||||||
|
accept: "*/*",
|
||||||
|
maxCount: 1,
|
||||||
|
disabled: true,
|
||||||
|
limit: 1,
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "remark",
|
||||||
|
label: "备注",
|
||||||
|
component: "BasicInput",
|
||||||
|
componentProps: {
|
||||||
|
placeholder: "备注信息",
|
||||||
|
type: "textarea",
|
||||||
|
rows: 3,
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "审批设置",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "spId",
|
||||||
|
label: "审批人",
|
||||||
|
component: "BasicJsPicker",
|
||||||
|
componentProps: {
|
||||||
|
multiple: true,
|
||||||
|
placeholder: "审批人",
|
||||||
|
title: "审批人",
|
||||||
|
searchPlaceholder: "输入教师姓名搜索",
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "ccId",
|
||||||
|
label: "抄送人",
|
||||||
|
component: "BasicJsPicker",
|
||||||
|
componentProps: {
|
||||||
|
multiple: true,
|
||||||
|
placeholder: "抄送人",
|
||||||
|
title: "抄送人",
|
||||||
|
searchPlaceholder: "输入教师姓名搜索",
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "tjrName",
|
||||||
|
label: "提交人",
|
||||||
|
component: "BasicInput",
|
||||||
|
componentProps: {
|
||||||
|
placeholder: "提交人",
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "tjrtime",
|
||||||
|
label: "提交时间",
|
||||||
|
component: "BasicInput",
|
||||||
|
componentProps: {
|
||||||
|
placeholder: "提交时间",
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "gwStatus",
|
||||||
|
label: "公文状态",
|
||||||
|
component: "BasicInput",
|
||||||
|
componentProps: {
|
||||||
|
placeholder: "公文状态",
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// 页面加载完成 - 使用onLoad获取路由参数
|
||||||
|
onLoad((options: any) => {
|
||||||
|
console.log('页面加载,路由参数:', options);
|
||||||
|
|
||||||
|
if (options && options.id) {
|
||||||
|
gwId.value = options.id;
|
||||||
|
getGwDetail();
|
||||||
|
} else {
|
||||||
|
console.error('未获取到公文ID参数');
|
||||||
|
uni.showToast({
|
||||||
|
title: "参数错误",
|
||||||
|
icon: "error",
|
||||||
|
});
|
||||||
|
// 返回上一页
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack();
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取公文详情
|
||||||
|
const getGwDetail = async () => {
|
||||||
|
try {
|
||||||
|
const response = await gwFindByIdApi({ id: gwId.value });
|
||||||
|
|
||||||
|
if (response && response.result) {
|
||||||
|
const gwData = response.result;
|
||||||
|
|
||||||
|
// 设置表单值
|
||||||
|
await setValue({
|
||||||
|
title: gwData.title || '',
|
||||||
|
docType: gwData.docType || '',
|
||||||
|
urgencyLevel: gwData.urgencyLevel || '',
|
||||||
|
fileUrl: gwData.fileUrl || '',
|
||||||
|
fileName: gwData.fileName || '',
|
||||||
|
fileFormat: gwData.fileFormat || '',
|
||||||
|
remark: gwData.remark || '',
|
||||||
|
spId: gwData.spId ? gwData.spId.split(',').filter((id: string) => id.trim()) : [],
|
||||||
|
ccId: gwData.ccId ? gwData.ccId.split(',').filter((id: string) => id.trim()) : [],
|
||||||
|
tjrName: gwData.tjrxm || gwData.tjrName || '',
|
||||||
|
tjrtime: gwData.tjrtime || '',
|
||||||
|
gwStatus: getStatusText(gwData.gwStatus || ''),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("获取公文详情失败:", error);
|
||||||
|
uni.showToast({
|
||||||
|
title: "获取详情失败",
|
||||||
|
icon: "error",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取状态文本
|
||||||
|
const getStatusText = (status: string) => {
|
||||||
|
const statusMap: Record<string, string> = {
|
||||||
|
'A': "暂存",
|
||||||
|
'B': "提交",
|
||||||
|
};
|
||||||
|
return statusMap[status] || "未知";
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
// 详情页面样式
|
||||||
|
</style>
|
||||||
@ -1,16 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<BasicLayout>
|
<view class="gw-list-page">
|
||||||
<view class="px-15 pb-15">
|
<!-- 列表组件 -->
|
||||||
<!-- 搜索和筛选 -->
|
<view class="list-component">
|
||||||
<view class="search-section">
|
<BasicListLayout @register="register" v-model="dataList">
|
||||||
|
<!-- 搜索和筛选组件放在top插槽中 -->
|
||||||
|
<template #top>
|
||||||
|
<view class="query-component">
|
||||||
|
<view class="search-card">
|
||||||
|
<!-- 搜索框 -->
|
||||||
|
<view class="search-item">
|
||||||
<BasicSearch
|
<BasicSearch
|
||||||
placeholder="搜索公文标题或编号"
|
placeholder="搜索公文标题或编号"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
|
class="search-input"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 筛选标签 -->
|
<!-- 筛选标签 -->
|
||||||
<view class="filter-section">
|
|
||||||
<view class="filter-tabs">
|
<view class="filter-tabs">
|
||||||
<view
|
<view
|
||||||
v-for="tab in filterTabs"
|
v-for="tab in filterTabs"
|
||||||
@ -23,91 +29,72 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 公文列表 -->
|
<template v-slot="{ data }">
|
||||||
<view class="gw-list">
|
<view class="gw-card">
|
||||||
<view
|
<view class="card-header">
|
||||||
v-for="item in filteredGwList"
|
<text class="gw-title">{{ data.title }}</text>
|
||||||
:key="item.id"
|
<text class="gw-status" :class="getStatusClass(data.gwStatus)">
|
||||||
class="gw-item"
|
{{ getStatusText(data.gwStatus) }}
|
||||||
@click="goToDetail(item)"
|
|
||||||
>
|
|
||||||
<view class="gw-header">
|
|
||||||
<view class="gw-title">{{ item.title }}</view>
|
|
||||||
<view class="gw-status" :class="getStatusClass(item.status)">
|
|
||||||
{{ getStatusText(item.status) }}
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="gw-info">
|
|
||||||
<view class="info-row">
|
|
||||||
<text class="label">编号:</text>
|
|
||||||
<text class="value">{{ item.gwNo }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="info-row">
|
|
||||||
<text class="label">类型:</text>
|
|
||||||
<text class="value">{{ item.gwType }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="info-row">
|
|
||||||
<text class="label">紧急程度:</text>
|
|
||||||
<text class="value urgency-tag" :class="getUrgencyClass(item.urgencyLevel)">
|
|
||||||
{{ getUrgencyText(item.urgencyLevel) }}
|
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="gw-footer">
|
<view class="card-body">
|
||||||
<view class="approver-info">
|
<view class="gw-info">
|
||||||
<text class="label">审批进度:</text>
|
<view class="info-item">
|
||||||
<text class="value">{{ getApproverProgress(item) }}</text>
|
<text class="info-label">类型:</text>
|
||||||
|
<text class="info-value">{{ data.docType }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="info-label">紧急程度:</text>
|
||||||
|
<text class="info-value urgency-tag" :class="getUrgencyClass(data.urgencyLevel)">
|
||||||
|
{{ getUrgencyText(data.urgencyLevel) }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="info-label">审批进度:</text>
|
||||||
|
<text class="info-value">{{ getApproverProgress(data) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="info-label">提交人:</text>
|
||||||
|
<text class="info-value">{{ data.tjrxm || '未知' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="info-label">提交时间:</text>
|
||||||
|
<text class="info-value">{{ formatTime(data.tjrtime || data.createdTime) }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="time-info">
|
|
||||||
<text class="label">创建时间:</text>
|
|
||||||
<text class="value">{{ formatTime(item.createdTime) }}</text>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="gw-actions">
|
<view class="card-footer">
|
||||||
|
<view class="footer-actions">
|
||||||
<u-button
|
<u-button
|
||||||
text="查看详情"
|
text="详情"
|
||||||
size="mini"
|
size="mini"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click.stop="goToDetail(item)"
|
@click="goToDetail(data)"
|
||||||
/>
|
/>
|
||||||
<u-button
|
<u-button
|
||||||
v-if="item.status === GwStatus.DRAFT"
|
v-if="data.gwStatus === 'B'"
|
||||||
text="编辑"
|
text="编辑"
|
||||||
size="mini"
|
size="mini"
|
||||||
@click.stop="editGw(item)"
|
@click="editGw(data)"
|
||||||
/>
|
/>
|
||||||
<u-button
|
<u-button
|
||||||
v-if="item.status === GwStatus.DRAFT"
|
v-if="data.gwStatus === 'B'"
|
||||||
text="删除"
|
text="删除"
|
||||||
size="mini"
|
size="mini"
|
||||||
type="error"
|
type="error"
|
||||||
@click.stop="deleteGw(item)"
|
@click="deleteGw(data)"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 空状态 -->
|
<!-- 新建公文按钮放在bottom插槽中 -->
|
||||||
<view v-if="filteredGwList.length === 0" class="empty-state">
|
|
||||||
<view class="empty-icon">📄</view>
|
|
||||||
<view class="empty-text">暂无公文数据</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 加载更多 -->
|
|
||||||
<view v-if="hasMore && filteredGwList.length > 0" class="load-more">
|
|
||||||
<u-button
|
|
||||||
text="加载更多"
|
|
||||||
@click="loadMore"
|
|
||||||
:loading="loading"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 底部操作按钮 -->
|
|
||||||
<template #bottom>
|
<template #bottom>
|
||||||
<view class="flex-row items-center pb-10 pt-5">
|
<view class="flex-row items-center pb-10 pt-5">
|
||||||
<u-button
|
<u-button
|
||||||
@ -118,15 +105,20 @@
|
|||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
</BasicLayout>
|
</BasicListLayout>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted } from "vue";
|
import { ref, computed, watch, onMounted } from "vue";
|
||||||
|
import { onShow } from "@dcloudio/uni-app";
|
||||||
import { navigateTo } from "@/utils/uniapp";
|
import { navigateTo } from "@/utils/uniapp";
|
||||||
import BasicSearch from "@/components/BasicSearch/Search.vue";
|
import BasicSearch from "@/components/BasicSearch/Search.vue";
|
||||||
import BasicLayout from "@/components/BasicLayout/Layout.vue";
|
import BasicLayout from "@/components/BasicLayout/Layout.vue";
|
||||||
import { getGwListApi, deleteGwApi } from "@/api/routine/gw";
|
import BasicListLayout from "@/components/BasicListLayout/ListLayout.vue";
|
||||||
|
import { useLayout } from "@/components/BasicListLayout/hooks/useLayout";
|
||||||
|
import { gwFindPageApi, gwLogicDeleteApi } from "@/api/routine/gw";
|
||||||
import { GwStatus, UrgencyLevel, ApproverStatus } from "@/types/gw";
|
import { GwStatus, UrgencyLevel, ApproverStatus } from "@/types/gw";
|
||||||
import type { GwInfo, GwListItem } from "@/types/gw";
|
import type { GwInfo, GwListItem } from "@/types/gw";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
@ -134,36 +126,35 @@ import dayjs from "dayjs";
|
|||||||
// 筛选标签
|
// 筛选标签
|
||||||
const filterTabs = [
|
const filterTabs = [
|
||||||
{ key: "all", label: "全部" },
|
{ key: "all", label: "全部" },
|
||||||
{ key: "draft", label: "草稿" },
|
{ key: "A", label: "已提交" },
|
||||||
{ key: "pending", label: "待审批" },
|
{ key: "B", label: "草稿" },
|
||||||
{ key: "approved", label: "已通过" },
|
|
||||||
{ key: "rejected", label: "已驳回" },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const activeTab = ref("all");
|
const activeTab = ref("all");
|
||||||
const searchKeyword = ref("");
|
const searchKeyword = ref("");
|
||||||
const gwList = ref<GwListItem[]>([]);
|
|
||||||
const loading = ref(false);
|
// 使用 BasicListLayout
|
||||||
const hasMore = ref(true);
|
const [register, { reload, setParam }] = useLayout({
|
||||||
const page = ref(1);
|
api: gwFindPageApi,
|
||||||
const pageSize = 20;
|
componentProps: {
|
||||||
|
defaultPageSize: 20,
|
||||||
|
},
|
||||||
|
param: {
|
||||||
|
title: "",
|
||||||
|
gwStatus: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 数据列表
|
||||||
|
const dataList = ref<GwListItem[]>([]);
|
||||||
|
|
||||||
// 筛选后的公文列表
|
// 筛选后的公文列表
|
||||||
const filteredGwList = computed(() => {
|
const filteredGwList = computed(() => {
|
||||||
let list = gwList.value;
|
let list = dataList.value;
|
||||||
|
|
||||||
// 按状态筛选
|
// 按状态筛选
|
||||||
if (activeTab.value !== "all") {
|
if (activeTab.value !== "all") {
|
||||||
const statusMap: Record<string, GwStatus> = {
|
list = list.filter(item => item.gwStatus === activeTab.value);
|
||||||
draft: GwStatus.DRAFT,
|
|
||||||
pending: GwStatus.PENDING,
|
|
||||||
approved: GwStatus.APPROVED,
|
|
||||||
rejected: GwStatus.REJECTED,
|
|
||||||
};
|
|
||||||
const targetStatus = statusMap[activeTab.value];
|
|
||||||
if (targetStatus) {
|
|
||||||
list = list.filter(item => item.status === targetStatus);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 按关键词搜索
|
// 按关键词搜索
|
||||||
@ -171,7 +162,7 @@ const filteredGwList = computed(() => {
|
|||||||
const keyword = searchKeyword.value.toLowerCase();
|
const keyword = searchKeyword.value.toLowerCase();
|
||||||
list = list.filter(item =>
|
list = list.filter(item =>
|
||||||
item.title.toLowerCase().includes(keyword) ||
|
item.title.toLowerCase().includes(keyword) ||
|
||||||
item.gwNo.toLowerCase().includes(keyword)
|
(item.gwNo && item.gwNo.toLowerCase().includes(keyword))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,88 +172,34 @@ const filteredGwList = computed(() => {
|
|||||||
// 切换筛选标签
|
// 切换筛选标签
|
||||||
const switchTab = (tabKey: string) => {
|
const switchTab = (tabKey: string) => {
|
||||||
activeTab.value = tabKey;
|
activeTab.value = tabKey;
|
||||||
|
// 更新查询参数并重新加载
|
||||||
|
const gwStatus = tabKey === "all" ? "" : tabKey;
|
||||||
|
setParam({ gwStatus });
|
||||||
|
reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 搜索处理
|
// 搜索处理
|
||||||
const handleSearch = (keyword: string) => {
|
const handleSearch = (keyword: string) => {
|
||||||
searchKeyword.value = keyword;
|
searchKeyword.value = keyword;
|
||||||
};
|
// 更新查询参数并重新加载
|
||||||
|
setParam({ title: keyword });
|
||||||
// 获取公文列表
|
reload();
|
||||||
const getGwList = async (isLoadMore = false) => {
|
|
||||||
try {
|
|
||||||
loading.value = true;
|
|
||||||
|
|
||||||
if (!isLoadMore) {
|
|
||||||
page.value = 1;
|
|
||||||
hasMore.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 模拟API调用,实际开发时替换为真实API
|
|
||||||
const statusMap: Record<string, GwStatus> = {
|
|
||||||
draft: GwStatus.DRAFT,
|
|
||||||
pending: GwStatus.PENDING,
|
|
||||||
approved: GwStatus.APPROVED,
|
|
||||||
rejected: GwStatus.REJECTED,
|
|
||||||
};
|
|
||||||
const targetStatus = activeTab.value === "all" ? undefined : statusMap[activeTab.value];
|
|
||||||
|
|
||||||
// 模拟筛选逻辑
|
|
||||||
let filteredData = mockData;
|
|
||||||
if (targetStatus) {
|
|
||||||
filteredData = mockData.filter(item => item.status === targetStatus);
|
|
||||||
}
|
|
||||||
if (searchKeyword.value) {
|
|
||||||
const keyword = searchKeyword.value.toLowerCase();
|
|
||||||
filteredData = filteredData.filter(item =>
|
|
||||||
item.title.toLowerCase().includes(keyword) ||
|
|
||||||
item.gwNo.toLowerCase().includes(keyword)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 模拟分页
|
|
||||||
const startIndex = (page.value - 1) * pageSize;
|
|
||||||
const endIndex = startIndex + pageSize;
|
|
||||||
const result = {
|
|
||||||
list: filteredData.slice(startIndex, endIndex),
|
|
||||||
hasMore: endIndex < filteredData.length,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isLoadMore) {
|
|
||||||
gwList.value.push(...result.list);
|
|
||||||
} else {
|
|
||||||
gwList.value = result.list;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasMore.value = result.hasMore;
|
|
||||||
page.value++;
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error("获取公文列表失败:", error);
|
|
||||||
uni.showToast({
|
|
||||||
title: "获取列表失败",
|
|
||||||
icon: "error",
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 加载更多
|
|
||||||
const loadMore = () => {
|
|
||||||
if (!loading.value && hasMore.value) {
|
|
||||||
getGwList(true);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 跳转到详情页面
|
// 跳转到详情页面
|
||||||
const goToDetail = (item: GwListItem) => {
|
const goToDetail = (item: GwListItem) => {
|
||||||
navigateTo(`/pages/view/routine/gwlz/gwFlow?id=${item.id}`);
|
navigateTo(`/pages/view/routine/gwlz/gwDetail?id=${item.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 编辑公文
|
// 编辑公文
|
||||||
const editGw = (item: GwListItem) => {
|
const editGw = (item: GwListItem) => {
|
||||||
navigateTo(`/pages/view/routine/gwlz?id=${item.id}&mode=edit`);
|
const url = `/pages/view/routine/gwlz/gwAdd?id=${item.id}&mode=edit`;
|
||||||
|
|
||||||
|
// 先存储编辑参数到本地存储,确保页面跳转后能获取到
|
||||||
|
uni.setStorageSync('gwEditMode', 'edit');
|
||||||
|
uni.setStorageSync('gwEditId', item.id);
|
||||||
|
|
||||||
|
navigateTo(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 删除公文
|
// 删除公文
|
||||||
@ -273,14 +210,11 @@ const deleteGw = (item: GwListItem) => {
|
|||||||
success: async (res) => {
|
success: async (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
try {
|
try {
|
||||||
// 模拟删除API调用
|
// 调用软删除API - 需要传递 {ids: item.id} 格式的参数
|
||||||
await deleteGwApi(item.id);
|
await gwLogicDeleteApi({ ids: item.id });
|
||||||
|
|
||||||
// 从本地列表中移除
|
// 删除成功后刷新列表
|
||||||
const index = gwList.value.findIndex(gw => gw.id === item.id);
|
reload();
|
||||||
if (index > -1) {
|
|
||||||
gwList.value.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "删除成功",
|
title: "删除成功",
|
||||||
@ -305,141 +239,163 @@ const createNewGw = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 获取状态样式类
|
// 获取状态样式类
|
||||||
const getStatusClass = (status: GwStatus) => {
|
const getStatusClass = (status: string) => {
|
||||||
const statusMap: Record<GwStatus, string> = {
|
const statusMap: Record<string, string> = {
|
||||||
[GwStatus.DRAFT]: "status-draft",
|
'A': "status-submitted", // 已提交状态
|
||||||
[GwStatus.PENDING]: "status-pending",
|
'B': "status-draft", // 草稿状态
|
||||||
[GwStatus.APPROVED]: "status-approved",
|
|
||||||
[GwStatus.REJECTED]: "status-rejected",
|
|
||||||
};
|
};
|
||||||
return statusMap[status] || "status-default";
|
return statusMap[status] || "status-default";
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取状态文本
|
// 获取状态文本
|
||||||
const getStatusText = (status: GwStatus) => {
|
const getStatusText = (status: string) => {
|
||||||
const statusMap: Record<GwStatus, string> = {
|
const statusMap: Record<string, string> = {
|
||||||
[GwStatus.DRAFT]: "草稿",
|
'A': "已提交", // 已提交状态
|
||||||
[GwStatus.PENDING]: "待审批",
|
'B': "草稿", // 草稿状态
|
||||||
[GwStatus.APPROVED]: "已通过",
|
|
||||||
[GwStatus.REJECTED]: "已驳回",
|
|
||||||
};
|
};
|
||||||
return statusMap[status] || "未知";
|
return statusMap[status] || "未知";
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取紧急程度样式类
|
// 获取紧急程度样式类
|
||||||
const getUrgencyClass = (urgency: UrgencyLevel) => {
|
const getUrgencyClass = (urgency: string) => {
|
||||||
const urgencyMap: Record<UrgencyLevel, string> = {
|
const urgencyMap: Record<string, string> = {
|
||||||
[UrgencyLevel.LOW]: "urgency-low",
|
'low': "urgency-low",
|
||||||
[UrgencyLevel.NORMAL]: "urgency-normal",
|
'normal': "urgency-normal",
|
||||||
[UrgencyLevel.HIGH]: "urgency-high",
|
'high': "urgency-high",
|
||||||
[UrgencyLevel.URGENT]: "urgency-urgent",
|
'urgent': "urgency-urgent",
|
||||||
};
|
};
|
||||||
return urgencyMap[urgency] || "urgency-normal";
|
return urgencyMap[urgency] || "urgency-normal";
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取紧急程度文本
|
// 获取紧急程度文本
|
||||||
const getUrgencyText = (urgency: UrgencyLevel) => {
|
const getUrgencyText = (urgency: string) => {
|
||||||
const urgencyMap: Record<UrgencyLevel, string> = {
|
const urgencyMap: Record<string, string> = {
|
||||||
[UrgencyLevel.LOW]: "普通",
|
'low': "普通",
|
||||||
[UrgencyLevel.NORMAL]: "一般",
|
'normal': "一般",
|
||||||
[UrgencyLevel.HIGH]: "紧急",
|
'high': "紧急",
|
||||||
[UrgencyLevel.URGENT]: "特急",
|
'urgent': "特急",
|
||||||
};
|
};
|
||||||
return urgencyMap[urgency] || "一般";
|
return urgencyMap[urgency] || "一般";
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取审批进度
|
// 获取审批进度
|
||||||
const getApproverProgress = (item: GwListItem) => {
|
const getApproverProgress = (item: GwListItem) => {
|
||||||
if (!item.approvers || item.approvers.length === 0) {
|
let spCount = 0;
|
||||||
return "无审批人";
|
let ccCount = 0;
|
||||||
|
|
||||||
|
// 统计审批人数量 - 处理逗号分隔的字符串或数组
|
||||||
|
if (item.spId) {
|
||||||
|
if (Array.isArray(item.spId)) {
|
||||||
|
spCount = item.spId.length;
|
||||||
|
} else {
|
||||||
|
// 如果是逗号分隔的字符串,按逗号分割并统计
|
||||||
|
const spIdStr = String(item.spId);
|
||||||
|
if (spIdStr.trim()) {
|
||||||
|
spCount = spIdStr.split(',').filter((id: string) => id.trim()).length;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const total = item.approvers.length;
|
// 统计抄送人数量 - 处理逗号分隔的字符串或数组
|
||||||
const approved = item.approvers.filter((a: any) => a.status === ApproverStatus.APPROVED).length;
|
if (item.ccId) {
|
||||||
const rejected = item.approvers.filter((a: any) => a.status === ApproverStatus.REJECTED).length;
|
if (Array.isArray(item.ccId)) {
|
||||||
|
ccCount = item.ccId.length;
|
||||||
if (rejected > 0) {
|
} else {
|
||||||
return `已驳回 (${rejected}/${total})`;
|
// 如果是逗号分隔的字符串,按逗号分割并统计
|
||||||
|
const ccIdStr = String(item.ccId);
|
||||||
|
if (ccIdStr.trim()) {
|
||||||
|
ccCount = ccIdStr.split(',').filter((id: string) => id.trim()).length;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${approved}/${total} 已审批`;
|
if (spCount === 0 && ccCount === 0) {
|
||||||
|
return "无审批人和抄送人";
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = "";
|
||||||
|
if (spCount > 0) {
|
||||||
|
result += `${spCount}个审批人`;
|
||||||
|
}
|
||||||
|
if (ccCount > 0) {
|
||||||
|
if (result) result += ",";
|
||||||
|
result += `${ccCount}个抄送人`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 格式化时间
|
// 格式化时间
|
||||||
const formatTime = (time: string | Date) => {
|
const formatTime = (time: string | Date | undefined) => {
|
||||||
|
if (!time) return '暂无';
|
||||||
return dayjs(time).format("MM-DD HH:mm");
|
return dayjs(time).format("MM-DD HH:mm");
|
||||||
};
|
};
|
||||||
|
|
||||||
// 模拟数据用于开发测试
|
// 监听数据变化
|
||||||
const mockData: GwListItem[] = [
|
watch(dataList, (val) => {
|
||||||
{
|
// 数据变化监听
|
||||||
id: "1",
|
});
|
||||||
title: "关于2024年教学工作计划的通知",
|
|
||||||
gwNo: "GW2024001",
|
|
||||||
gwType: "通知",
|
|
||||||
urgencyLevel: UrgencyLevel.NORMAL,
|
|
||||||
status: GwStatus.PENDING,
|
|
||||||
createdBy: "admin",
|
|
||||||
createdTime: new Date(),
|
|
||||||
files: [],
|
|
||||||
approvers: [
|
|
||||||
{ id: "1", userId: "user1", userName: "审批人1", deptId: "dept1", deptName: "部门1", order: 1, status: ApproverStatus.APPROVED },
|
|
||||||
{ id: "2", userId: "user2", userName: "审批人2", deptId: "dept2", deptName: "部门2", order: 2, status: ApproverStatus.PENDING },
|
|
||||||
],
|
|
||||||
ccUsers: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "2",
|
|
||||||
title: "2024年春季学期课程安排",
|
|
||||||
gwNo: "GW2024002",
|
|
||||||
gwType: "安排",
|
|
||||||
urgencyLevel: UrgencyLevel.HIGH,
|
|
||||||
status: GwStatus.APPROVED,
|
|
||||||
createdBy: "admin",
|
|
||||||
createdTime: new Date(Date.now() - 86400000),
|
|
||||||
files: [],
|
|
||||||
approvers: [
|
|
||||||
{ id: "3", userId: "user1", userName: "审批人1", deptId: "dept1", deptName: "部门1", order: 1, status: ApproverStatus.APPROVED },
|
|
||||||
{ id: "4", userId: "user2", userName: "审批人2", deptId: "dept2", deptName: "部门2", order: 2, status: ApproverStatus.APPROVED },
|
|
||||||
],
|
|
||||||
ccUsers: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "3",
|
|
||||||
title: "学生宿舍管理规定修订稿",
|
|
||||||
gwNo: "GW2024003",
|
|
||||||
gwType: "规定",
|
|
||||||
urgencyLevel: UrgencyLevel.LOW,
|
|
||||||
status: GwStatus.DRAFT,
|
|
||||||
createdBy: "admin",
|
|
||||||
createdTime: new Date(Date.now() - 172800000),
|
|
||||||
files: [],
|
|
||||||
approvers: [],
|
|
||||||
ccUsers: [],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
|
// 页面显示时重新加载数据
|
||||||
|
onShow(() => {
|
||||||
|
reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 页面加载时也加载一次数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 使用模拟数据初始化列表
|
reload();
|
||||||
gwList.value = mockData;
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.search-section {
|
.gw-list-page {
|
||||||
margin-bottom: 15px;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #f5f7fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-section {
|
.list-component {
|
||||||
margin-bottom: 20px;
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.filter-tabs {
|
.query-component {
|
||||||
display: flex;
|
padding: 15px;
|
||||||
background: #f5f5f5;
|
background-color: white;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-card {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-item {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 4px;
|
padding: 0 15px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
.filter-tab {
|
.filter-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-tab {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
@ -447,129 +403,191 @@ onMounted(() => {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #666;
|
color: #666;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background: #007aff;
|
background-color: #007aff;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
border-color: #007aff;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.gw-list {
|
.gw-card {
|
||||||
.gw-item {
|
background-color: white;
|
||||||
background: white;
|
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
.gw-header {
|
&:active {
|
||||||
|
transform: translateY(1px);
|
||||||
|
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.gw-title {
|
.gw-title {
|
||||||
flex: 1;
|
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 600;
|
||||||
color: #333;
|
color: #2c3e50;
|
||||||
line-height: 1.4;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gw-status {
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 12px;
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
&.status-draft { background: #f0f0f0; color: #666; }
|
|
||||||
&.status-pending { background: #fff7e6; color: #fa8c16; }
|
|
||||||
&.status-approved { background: #f6ffed; color: #52c41a; }
|
|
||||||
&.status-rejected { background: #fff2f0; color: #ff4d4f; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.gw-info {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
|
|
||||||
.info-row {
|
|
||||||
display: flex;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
|
|
||||||
.label {
|
|
||||||
width: 70px;
|
|
||||||
color: #666;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
line-height: 1.4;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gw-status {
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
&.status-draft {
|
||||||
|
background: linear-gradient(135deg, #ffa726 0%, #ff9800 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
&.status-submitted {
|
||||||
|
background: linear-gradient(135deg, #66bb6a 0%, #4caf50 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
&.status-approved {
|
||||||
|
background: linear-gradient(135deg, #66bb6a 0%, #4caf50 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
&.status-rejected {
|
||||||
|
background: linear-gradient(135deg, #ef5350 0%, #e53935 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gw-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
width: 80px;
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-right: 8px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
color: #333;
|
color: #333;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.urgency-tag {
|
.urgency-tag {
|
||||||
padding: 2px 6px;
|
padding: 4px 8px;
|
||||||
border-radius: 4px;
|
border-radius: 12px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
&.urgency-low { background: #f6ffed; color: #52c41a; }
|
&.urgency-low {
|
||||||
&.urgency-normal { background: #f0f0f0; color: #666; }
|
background: linear-gradient(135deg, #66bb6a 0%, #4caf50 100%);
|
||||||
&.urgency-high { background: #fff7e6; color: #fa8c16; }
|
color: white;
|
||||||
&.urgency-urgent { background: #fff2f0; color: #ff4d4f; }
|
|
||||||
}
|
}
|
||||||
|
&.urgency-normal {
|
||||||
|
background: linear-gradient(135deg, #90a4ae 0%, #78909c 100%);
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
&.urgency-high {
|
||||||
|
background: linear-gradient(135deg, #ffa726 0%, #ff9800 100%);
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
&.urgency-urgent {
|
||||||
|
background: linear-gradient(135deg, #ef5350 0%, #e53935 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.gw-footer {
|
.card-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: flex-end;
|
||||||
margin-bottom: 15px;
|
align-items: center;
|
||||||
font-size: 12px;
|
padding-top: 12px;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
.approver-info,
|
.footer-actions {
|
||||||
.time-info {
|
|
||||||
.label {
|
|
||||||
color: #666;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.gw-actions {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
justify-content: flex-end;
|
}
|
||||||
|
|
||||||
|
// 响应式优化
|
||||||
|
@media (max-width: 375px) {
|
||||||
|
.query-component {
|
||||||
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-card {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gw-card {
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header .gw-title {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-tabs {
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-tab {
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-state {
|
// 加载动画
|
||||||
text-align: center;
|
.gw-card {
|
||||||
padding: 60px 20px;
|
animation: fadeInUp 0.3s ease-out;
|
||||||
|
|
||||||
.empty-icon {
|
|
||||||
font-size: 48px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-text {
|
|
||||||
color: #999;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.load-more {
|
@keyframes fadeInUp {
|
||||||
text-align: center;
|
from {
|
||||||
margin-top: 20px;
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user