feat(pwlProject): 更新项目管理功能并优化表格展示- 增加了项目统计和导入功能

-优化了项目列表页面的展示结构和字段显示- 调整了编辑弹窗的宽度以适应更多内容
- 添加了highlight.js依赖以支持代码高亮
- 更新了表格列配置,增加了嵌套表头和固定列- 改进了用户、底稿、签字等信息的展示方式
-优化了状态标签和时间格式的显示- 增加了页面标题和返回功能
- 更新了API参数类型定义,增加type和itemYear字段
This commit is contained in:
2025-10-27 11:13:38 +08:00
parent 645d2203d3
commit 8c711e066b
8 changed files with 1381 additions and 1068 deletions

View File

@@ -73,6 +73,7 @@
"eslint-define-config": "^1.7.0", "eslint-define-config": "^1.7.0",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.4.0", "eslint-plugin-vue": "^9.4.0",
"highlight.js": "^11.11.1",
"less": "^4.1.3", "less": "^4.1.3",
"postcss": "^8.4.39", "postcss": "^8.4.39",
"prettier": "^2.7.1", "prettier": "^2.7.1",

3
pnpm-lock.yaml generated
View File

@@ -186,6 +186,9 @@ importers:
eslint-plugin-vue: eslint-plugin-vue:
specifier: ^9.4.0 specifier: ^9.4.0
version: 9.33.0(eslint@8.57.1) version: 9.33.0(eslint@8.57.1)
highlight.js:
specifier: ^11.11.1
version: 11.11.1
less: less:
specifier: ^4.1.3 specifier: ^4.1.3
version: 4.4.2 version: 4.4.2

View File

@@ -104,3 +104,28 @@ export async function getPwlProject(id: number) {
} }
return Promise.reject(new Error(res.data.message)); return Promise.reject(new Error(res.data.message));
} }
export async function pwlProjectCount() {
const res = await request.get('/pwl/pwl-project/');
if (res.data.code === 0) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 文章批量导入
*/
export async function importPwlProject(file: File) {
const formData = new FormData();
formData.append('file', file);
const res = await request.post<ApiResult<unknown>>(
MODULES_API_URL + '/pwl/pwl-project/import',
formData
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}

View File

@@ -105,5 +105,7 @@ export interface PwlProject {
*/ */
export interface PwlProjectParam extends PageParam { export interface PwlProjectParam extends PageParam {
id?: number; id?: number;
type?: string;
itemYear?: string;
keywords?: string; keywords?: string;
} }

View File

@@ -1,11 +1,11 @@
<!-- 编辑弹窗 --> <!-- 编辑弹窗 -->
<template> <template>
<ele-modal <ele-modal
:width="800" :width="1000"
:visible="visible" :visible="visible"
:maskClosable="false" :maskClosable="false"
:maxable="maxable" :maxable="maxable"
:title="isUpdate ? '编辑卫兰的项目项目系统' : '添加卫兰的项目项目系统'" :title="isUpdate ? '编辑项目' : '添加项目'"
:body-style="{ paddingBottom: '28px' }" :body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible" @update:visible="updateVisible"
@ok="save" @ok="save"
@@ -19,9 +19,28 @@
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' } styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
" "
> >
<a-form-item label="审计单位" name="name"> <a-form-item label="项目类型" name="type">
<DictSelect
dict-code="Type"
:width="200"
:show-search="true"
placeholder="项目类型"
v-model:value="form.type"
@done="chooseType"
/>
</a-form-item>
<a-form-item label="报告时间" name="expirationTime">
<a-input <a-input
allow-clear allow-clear
style="width: 200px"
placeholder="请输入报告时间"
v-model:value="form.expirationTime"
/>
</a-form-item>
<a-form-item label="被审计单位" name="name">
<a-input
allow-clear
style="width: 400px"
placeholder="请输入审计单位" placeholder="请输入审计单位"
v-model:value="form.name" v-model:value="form.name"
/> />
@@ -29,257 +48,209 @@
<a-form-item label="报告编号" name="code"> <a-form-item label="报告编号" name="code">
<a-input <a-input
allow-clear allow-clear
style="width: 400px"
placeholder="请输入报告编号" placeholder="请输入报告编号"
v-model:value="form.code" v-model:value="form.code"
/> />
</a-form-item> </a-form-item>
<a-form-item label="上级id, 0是顶级" name="parentId"> <a-divider orientation="left" style="margin-top: 50px">项目信息</a-divider>
<a-form-item label="开票单位/汇款人" name="itemName">
<a-input <a-input
allow-clear allow-clear
placeholder="请输入上级id, 0是顶级" style="width: 400px"
v-model:value="form.parentId" placeholder="请输入项目信息-开票单位/汇款人"
v-model:value="form.itemName"
/> />
</a-form-item> </a-form-item>
<a-form-item label="项目类型" name="type"> <a-form-item label="所属年度" name="itemYear">
<a-input-number
allow-clear
style="width: 200px"
placeholder="请输入项目信息-年度"
v-model:value="form.itemYear"
/>
</a-form-item>
<a-form-item label="类型" name="itemType">
<a-input <a-input
allow-clear allow-clear
placeholder="请输入项目类型" style="width: 200px"
v-model:value="form.type" placeholder="请输入类型"
v-model:value="form.itemType"
/> />
</a-form-item> </a-form-item>
<a-form-item <a-form-item label="审计意见" name="itemOpinion">
label="项目图标"
name="image">
<SelectFile
:placeholder="`请选择图片`"
:limit="1"
:data="images"
@done="chooseImage"
@del="onDeleteItem"
/>
</a-form-item>
<a-form-item label="二维码" name="qrcode">
<a-input <a-input
allow-clear allow-clear
placeholder="请输入二维码" style="width: 200px"
v-model:value="form.qrcode" placeholder="请输入审计意见"
/> v-model:value="form.itemOpinion"
</a-form-item>
<a-form-item label="链接地址" name="url">
<a-input
allow-clear
placeholder="请输入链接地址"
v-model:value="form.url"
/>
</a-form-item>
<a-form-item label="应用截图" name="images">
<a-input
allow-clear
placeholder="请输入应用截图"
v-model:value="form.images"
/>
</a-form-item>
<a-form-item label="底稿情况" name="files">
<a-input
allow-clear
placeholder="请输入底稿情况"
v-model:value="form.files"
/>
</a-form-item>
<a-form-item label="应用介绍" name="content">
<a-input
allow-clear
placeholder="请输入应用介绍"
v-model:value="form.content"
/> />
</a-form-item> </a-form-item>
<a-form-item label="年末资产总额(万元)" name="totalAssets"> <a-form-item label="年末资产总额(万元)" name="totalAssets">
<a-input <a-input-number
allow-clear allow-clear
style="width: 200px"
placeholder="请输入年末资产总额(万元)" placeholder="请输入年末资产总额(万元)"
v-model:value="form.totalAssets" v-model:value="form.totalAssets"
/> />
</a-form-item> </a-form-item>
<a-form-item label="合同金额" name="contractPrice"> <a-form-item label="合同金额" name="contractPrice">
<a-input <a-input-number
allow-clear allow-clear
style="width: 200px"
placeholder="请输入合同金额" placeholder="请输入合同金额"
v-model:value="form.contractPrice" v-model:value="form.contractPrice"
/> />
</a-form-item> </a-form-item>
<a-form-item label="实收金额" name="payPrice"> <a-form-item label="实收金额" name="payPrice">
<a-input <a-input-number
allow-clear allow-clear
style="width: 200px"
placeholder="请输入实收金额" placeholder="请输入实收金额"
v-model:value="form.payPrice" v-model:value="form.payPrice"
/> />
</a-form-item> </a-form-item>
<a-form-item label="软件定价" name="price"> <a-divider orientation="left" style="margin-top: 50px">到账信息</a-divider>
<a-form-item label="银行" name="bankName">
<a-input <a-input
allow-clear allow-clear
placeholder="请输入软件定价" style="width: 200px"
v-model:value="form.price" placeholder="请输入银行名称"
/>
</a-form-item>
<a-form-item label="是否推荐" name="recommend">
<a-input
allow-clear
placeholder="请输入是否推荐"
v-model:value="form.recommend"
/>
</a-form-item>
<a-form-item label="到期时间" name="expirationTime">
<a-input
allow-clear
placeholder="请输入到期时间"
v-model:value="form.expirationTime"
/>
</a-form-item>
<a-form-item label="项目信息-开票单位/汇款人" name="itemName">
<a-input
allow-clear
placeholder="请输入项目信息-开票单位/汇款人"
v-model:value="form.itemName"
/>
</a-form-item>
<a-form-item label="项目信息-年度" name="itemYear">
<a-input
allow-clear
placeholder="请输入项目信息-年度"
v-model:value="form.itemYear"
/>
</a-form-item>
<a-form-item label="项目信息-类型" name="itemType">
<a-input
allow-clear
placeholder="请输入项目信息-类型"
v-model:value="form.itemType"
/>
</a-form-item>
<a-form-item label="项目信息-审计意见" name="itemOpinion">
<a-input
allow-clear
placeholder="请输入项目信息-审计意见"
v-model:value="form.itemOpinion"
/>
</a-form-item>
<a-form-item label="到账信息-银行名称" name="bankName">
<a-input
allow-clear
placeholder="请输入到账信息-银行名称"
v-model:value="form.bankName" v-model:value="form.bankName"
/> />
</a-form-item> </a-form-item>
<a-form-item label="到账日期" name="bankPayTime"> <a-form-item label="日期" name="bankPayTime">
<a-input <a-input
allow-clear allow-clear
style="width: 200px"
placeholder="请输入到账日期" placeholder="请输入到账日期"
v-model:value="form.bankPayTime" v-model:value="form.bankPayTime"
/> />
</a-form-item> </a-form-item>
<a-form-item label="到账金额" name="bankPrice"> <a-form-item label="金额" name="bankPrice">
<a-input <a-input-number
allow-clear allow-clear
style="width: 200px"
placeholder="请输入到账金额" placeholder="请输入到账金额"
v-model:value="form.bankPrice" v-model:value="form.bankPrice"
/> />
</a-form-item> </a-form-item>
<a-form-item label="发票类型" name="invoiceType"> <a-divider orientation="left" style="margin-top: 50px">开票信息</a-divider>
<a-input <a-form-item label="日期" name="invoiceTime">
allow-clear
placeholder="请输入发票类型"
v-model:value="form.invoiceType"
/>
</a-form-item>
<a-form-item label="开票日期" name="invoiceTime">
<a-input <a-input
allow-clear allow-clear
style="width: 200px"
placeholder="请输入开票日期" placeholder="请输入开票日期"
v-model:value="form.invoiceTime" v-model:value="form.invoiceTime"
/> />
</a-form-item> </a-form-item>
<a-form-item label="开票金额" name="invoicePrice"> <a-form-item label="金额" name="invoicePrice">
<a-input <a-input-number
allow-clear allow-clear
style="width: 200px"
placeholder="请输入开票金额" placeholder="请输入开票金额"
v-model:value="form.invoicePrice" v-model:value="form.invoicePrice"
/> />
</a-form-item> </a-form-item>
<a-form-item label="类型" name="invoiceType">
<DictSelect
dict-code="InvoiceType"
:width="200"
:show-search="true"
placeholder="项目类型"
v-model:value="form.invoiceType"
/>
</a-form-item>
<a-divider orientation="left" style="margin-top: 50px">其他设置</a-divider>
<a-form-item label="报告份数" name="reportNum"> <a-form-item label="报告份数" name="reportNum">
<a-input <a-input
allow-clear allow-clear
style="width: 200px"
placeholder="请输入报告份数" placeholder="请输入报告份数"
v-model:value="form.reportNum" v-model:value="form.reportNum"
/> />
</a-form-item> </a-form-item>
<a-form-item label="底稿人员" name="draftUserId"> <a-form-item label="底稿人员" name="draftUserId">
<a-input <a-select
allow-clear show-search
placeholder="请输入底稿人员" :allow-clear="true"
optionFilterProp="label"
v-model:value="form.draftUserId" v-model:value="form.draftUserId"
/> mode="tags"
style="width: 100%"
placeholder="选择底稿人员"
:options="userList"
@search="handleSearch"
@change="handleDraftUserId"
></a-select>
</a-form-item> </a-form-item>
<a-form-item label="底稿人员" name="draftUser"> <a-form-item label="参与成员" name="userIds" extra="配置项目权限->参与成员可见">
<a-input <a-select
allow-clear show-search
placeholder="请输入底稿人员" :allow-clear="true"
v-model:value="form.draftUser" optionFilterProp="label"
/>
</a-form-item>
<a-form-item label="参与成员" name="userIds">
<a-input
allow-clear
placeholder="请输入参与成员"
v-model:value="form.userIds" v-model:value="form.userIds"
/> mode="tags"
</a-form-item> style="width: 100%"
<a-form-item label="参与成员" name="users"> placeholder="选择参与成员"
<a-input :options="userList"
allow-clear @change="handleUsers"
placeholder="请输入参与成员" ></a-select>
v-model:value="form.users"
/>
</a-form-item> </a-form-item>
<a-form-item label="签字注会" name="signUserId"> <a-form-item label="签字注会" name="signUserId">
<a-input <a-select
allow-clear show-search
placeholder="请输入签字注会" :allow-clear="true"
optionFilterProp="label"
v-model:value="form.signUserId" v-model:value="form.signUserId"
/> mode="tags"
</a-form-item> style="width: 100%"
<a-form-item label="签字注会" name="signUser"> placeholder="选择参与成员"
<a-input :options="userList"
allow-clear @change="handleSignUser"
placeholder="请输入签字注会" ></a-select>
v-model:value="form.signUser"
/>
</a-form-item> </a-form-item>
<a-form-item label="展业人员" name="saleUserId"> <a-form-item label="展业人员" name="saleUserId">
<a-input <a-select
allow-clear show-search
placeholder="请输入展业人员" :allow-clear="true"
optionFilterProp="label"
v-model:value="form.saleUserId" v-model:value="form.saleUserId"
/> mode="tags"
</a-form-item> style="width: 100%"
<a-form-item label="展业人员" name="saleUser"> placeholder="选择参与成员"
<a-input :options="userList"
allow-clear @change="handleSaleUser"
placeholder="请输入展业人员" ></a-select>
v-model:value="form.saleUser"
/>
</a-form-item>
<a-form-item label="纸质底稿完成情况" name="paper">
<a-input
allow-clear
placeholder="请输入纸质底稿完成情况"
v-model:value="form.paper"
/>
</a-form-item> </a-form-item>
<a-form-item label="电子底稿完成情况" name="electron"> <a-form-item label="电子底稿完成情况" name="electron">
<a-input <a-space direction="vertical">
allow-clear <a-radio-group v-model:value="form.electron">
placeholder="请输入电子底稿完成情况" <a-radio :value="1">未完成</a-radio>
v-model:value="form.electron" <a-radio :value="0">已完成</a-radio>
</a-radio-group>
</a-space>
</a-form-item>
<a-form-item label="纸质底稿完成情况" name="paper">
<a-space direction="vertical">
<a-radio-group v-model:value="form.paper">
<a-radio :value="1">未完成</a-radio>
<a-radio :value="0">已完成</a-radio>
</a-radio-group>
</a-space>
</a-form-item>
<a-form-item label="报告完成状态" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="1">未完成</a-radio>
<a-radio :value="0">已完成</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="排序" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/> />
</a-form-item> </a-form-item>
<a-form-item label="备注" name="comments"> <a-form-item label="备注" name="comments">
@@ -290,49 +261,6 @@
v-model:value="form.comments" v-model:value="form.comments"
/> />
</a-form-item> </a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="状态, 0正常, 1冻结" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="是否删除, 0否, 1是" name="deleted">
<a-input
allow-clear
placeholder="请输入是否删除, 0否, 1是"
v-model:value="form.deleted"
/>
</a-form-item>
<a-form-item label="客户ID" name="userId">
<a-input
allow-clear
placeholder="请输入客户ID"
v-model:value="form.userId"
/>
</a-form-item>
<a-form-item label="修改时间" name="updateTime">
<a-input
allow-clear
placeholder="请输入修改时间"
v-model:value="form.updateTime"
/>
</a-form-item>
<a-form-item label="知识库ID" name="kbId">
<a-input
allow-clear
placeholder="请输入知识库ID"
v-model:value="form.kbId"
/>
</a-form-item>
</a-form> </a-form>
</ele-modal> </ele-modal>
</template> </template>
@@ -347,7 +275,11 @@
import {storeToRefs} from 'pinia'; import {storeToRefs} from 'pinia';
import {ItemType} from 'ele-admin-pro/es/ele-image-upload/types'; import {ItemType} from 'ele-admin-pro/es/ele-image-upload/types';
import {FormInstance} from 'ant-design-vue/es/form'; import {FormInstance} from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model'; import DictSelect from "@/components/DictSelect/index.vue";
import {DictData} from "@/api/system/dict-data/model";
import {pageUsers} from "@/api/system/user";
import {User} from "@/api/system/user/model";
import {hasRole} from "@/utils/permission";
// 是否是修改 // 是否是修改
const isUpdate = ref(false); const isUpdate = ref(false);
@@ -375,6 +307,12 @@
// 表格选中数据 // 表格选中数据
const formRef = ref<FormInstance | null>(null); const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]); const images = ref<ItemType[]>([]);
const userList = ref<User[]>([]);
const draftUser = ref<any[]>([]);
const users = ref<any[]>([]);
const signUser = ref<any[]>([]);
const saleUser = ref<any[]>([]);
const keywords = ref<string>();
// 用户信息 // 用户信息
const form = reactive<PwlProject>({ const form = reactive<PwlProject>({
@@ -406,29 +344,23 @@
invoiceTime: undefined, invoiceTime: undefined,
invoicePrice: undefined, invoicePrice: undefined,
reportNum: undefined, reportNum: undefined,
draftUserId: undefined, draftUserId: [],
draftUser: undefined, draftUser: [],
userIds: undefined, userIds: [],
users: undefined, users: [],
signUserId: undefined, signUserId: [],
signUser: undefined, signUser: [],
saleUserId: undefined, saleUserId: [],
saleUser: undefined, saleUser: [],
paper: undefined,
electron: undefined,
comments: undefined, comments: undefined,
sortNumber: undefined, electron: undefined,
paper: undefined,
status: undefined, status: undefined,
deleted: undefined, deleted: undefined,
userId: undefined, userId: undefined,
tenantId: undefined, tenantId: undefined,
createTime: undefined, createTime: undefined,
updateTime: undefined, updateTime: undefined,
kbId: undefined,
pwlProjectId: undefined,
pwlProjectName: '',
status: 0,
comments: '',
sortNumber: 100 sortNumber: 100
}); });
@@ -439,28 +371,132 @@
// 表单验证规则 // 表单验证规则
const rules = reactive({ const rules = reactive({
pwlProjectName: [ type: [
{ {
required: true, required: true,
type: 'string', type: 'string',
message: '请填写卫兰的项目项目系统名称', message: '请选择项目类型',
trigger: 'change'
}
],
name: [
{
required: true,
type: 'string',
message: '请填写项目名称',
trigger: 'blur' trigger: 'blur'
} }
],
draftUserId: [
{
required: true,
type: 'array',
message: '请选择底稿人员',
trigger: 'change'
}
],
userIds: [
{
required: true,
type: 'array',
message: '请选择参与成员',
trigger: 'change'
}
],
saleUserId:[
{
required: true,
type: 'array',
message: '请选择展业人员',
trigger: 'change'
}
],
electron: [
{
required: true,
type: 'number',
message: '电子底稿是否已完成',
trigger: 'change'
}
],
paper: [
{
required: true,
type: 'number',
message: '纸质底稿是否已完成',
trigger: 'change'
}
],
status: [
{
required: true,
type: 'number',
message: '请选择项目状态',
trigger: 'change'
}
] ]
}); });
const chooseImage = (data: FileRecord) => { const chooseType = (data: DictData) => {
images.value.push({ console.log(data);
uid: data.id, form.type = data.dictDataName;
url: data.path, }
status: 'done' // const chooseImage = (data: FileRecord) => {
}); // images.value.push({
form.image = data.path; // uid: data.id,
}; // url: data.path,
// status: 'done'
// });
// form.image = data.path;
// };
//
// const onDeleteItem = (index: number) => {
// images.value.splice(index, 1);
// form.image = '';
// };
const onDeleteItem = (index: number) => { const handleSearch = (item) => {
images.value.splice(index, 1); keywords.value = item
form.image = ''; fetchUsers();
}
const handleDraftUserId = (userId: number, item: User[]) => {
draftUser.value = [];
item.map(d => {
draftUser.value.push(d.realName);
});
console.log(userId)
}
const handleUsers = (userId: number, item: User[]) => {
users.value = [];
item.map(d => {
users.value.push(d.realName);
});
console.log(userId)
}
const handleSignUser = (userId: number, item: User[]) => {
signUser.value = [];
item.map(d => {
signUser.value.push(d.realName);
});
console.log(userId)
}
const handleSaleUser = (userId: number, item: User[]) => {
saleUser.value = [];
item.map(d => {
saleUser.value.push(d.realName);
});
console.log(userId)
}
const fetchUsers = () => {
pageUsers({keywords: keywords.value,limit: 100}).then(res => {
userList.value = res?.list.map(d => {
d.label = d.realName;
d.value = d.userId;
return d;
}) || [];
})
}; };
const {resetFields} = useForm(form, rules); const {resetFields} = useForm(form, rules);
@@ -475,8 +511,29 @@
.then(() => { .then(() => {
loading.value = true; loading.value = true;
const formData = { const formData = {
...form ...form,
draftUserId: JSON.stringify(form.draftUserId),
draftUser: JSON.stringify(Array.from(new Set(draftUser.value))),
userIds: JSON.stringify(form.userIds),
users: JSON.stringify(Array.from(new Set(users.value))),
signUserId: JSON.stringify(form.signUserId),
signUser: JSON.stringify(Array.from(new Set(signUser.value))),
saleUserId: JSON.stringify(form.saleUserId),
saleUser: JSON.stringify(Array.from(new Set(saleUser.value))),
}; };
if (!hasRole('superAdmin')) {
form.payPrice = undefined;
}
if (!hasRole('admin')) {
form.contractPrice = undefined;
form.bankName = undefined;
form.bankPayTime = undefined;
form.bankPrice = undefined;
form.invoiceType = undefined;
form.invoiceTime = undefined;
form.invoicePrice = undefined;
}
console.log(formData);
const saveOrUpdate = isUpdate.value ? updatePwlProject : addPwlProject; const saveOrUpdate = isUpdate.value ? updatePwlProject : addPwlProject;
saveOrUpdate(formData) saveOrUpdate(formData)
.then((msg) => { .then((msg) => {
@@ -490,12 +547,14 @@
message.error(e.message); message.error(e.message);
}); });
}) })
.catch(() => {}); .catch(() => {
});
}; };
watch( watch(
() => props.visible, () => props.visible,
(visible) => { (visible) => {
fetchUsers();
if (visible) { if (visible) {
images.value = []; images.value = [];
if (props.data) { if (props.data) {
@@ -507,12 +566,67 @@
status: 'done' status: 'done'
}) })
} }
if (props.data.draftUserId) {
form.draftUserId = JSON.parse(props.data.draftUserId);
}else {
form.draftUserId = [];
}
// 清空所有用户数组,避免数据累积
draftUser.value = [];
users.value = [];
signUser.value = [];
saleUser.value = [];
if (props.data.draftUser) {
const arr = JSON.parse(props.data.draftUser) || [];
draftUser.value = [...arr]; // 直接赋值避免push累积
}
if (props.data.userIds) {
form.userIds = JSON.parse(props.data.userIds) || [];
}else {
form.userIds = [];
}
if (props.data.users) {
const arr = JSON.parse(props.data.users) || [];
users.value = [...arr]; // 直接赋值避免push累积
}
if (props.data.signUserId) {
form.signUserId = JSON.parse(props.data.signUserId) || [];
}else {
form.signUserId = [];
}
if (props.data.signUser) {
const arr = JSON.parse(props.data.signUser) || [];
signUser.value = [...arr]; // 直接赋值避免push累积
}
if (props.data.saleUserId) {
form.saleUserId = JSON.parse(props.data.saleUserId) || [];
}else {
form.saleUserId = [];
}
if (props.data.saleUser) {
const arr = JSON.parse(props.data.saleUser) || [];
saleUser.value = [...arr]; // 直接赋值避免push累积
}
isUpdate.value = true; isUpdate.value = true;
} else { } else {
isUpdate.value = false; isUpdate.value = false;
// 新增时也要清空用户数组
draftUser.value = [];
users.value = [];
signUser.value = [];
saleUser.value = [];
} }
} else { } else {
resetFields(); resetFields();
// 弹窗关闭时清空所有用户数组
draftUser.value = [];
users.value = [];
signUser.value = [];
saleUser.value = [];
} }
}, },
{immediate: true} {immediate: true}

View File

@@ -7,13 +7,57 @@
</template> </template>
<span>添加</span> <span>添加</span>
</a-button> </a-button>
<a-button
danger
v-if="hasRole('superAdmin')"
type="primary"
class="ele-btn-icon"
:disabled="selection?.length === 0"
@click="removeBatch"
>
<template #icon>
<DeleteOutlined/>
</template>
<span>批量删除</span>
</a-button>
<DictSelect
dict-code="Type"
:width="200"
:show-search="true"
placeholder="项目类型"
v-model:value="where.type"
@done="chooseType"
/>
<a-date-picker v-model:value="where.itemYear" value-format="YYYY" picker="year" @change="onYear" />
<a-input-search
allow-clear
placeholder="请输入关键词"
v-model:value="where.keywords"
@pressEnter="search"
@search="search"
/>
<a-button @click="reset">重置</a-button>
<a-button type="text" v-if="hasRole('superAdmin')" @click="handleExport">导出xls</a-button>
<a-button type="text" v-if="hasRole('superAdmin')" @click="openImport">导入xls</a-button>
</a-space> </a-space>
<!-- 导入弹窗 -->
<Import v-model:visible="showImport" @done="search"/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue'; import {DeleteOutlined, PlusOutlined} from '@ant-design/icons-vue';
import type {GradeParam} from '@/api/user/grade/model'; import type {GradeParam} from '@/api/user/grade/model';
import { watch } from 'vue'; import {watch, ref} from 'vue';
import {hasRole} from "@/utils/permission";
import dayjs from 'dayjs';
import {message} from 'ant-design-vue';
import {utils, writeFile} from 'xlsx';
import {PwlProject, PwlProjectParam} from "@/api/pwl/pwlProject/model";
import useSearch from "@/utils/use-search";
import {listPwlProject} from "@/api/pwl/pwlProject";
import Import from "./Import.vue";
import DictSelect from "@/components/DictSelect/index.vue";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@@ -34,9 +78,154 @@
const add = () => { const add = () => {
emit('add'); emit('add');
}; };
// 日期范围选择
const dateRange = ref<[string, string]>(['', '']);
const loading = ref(false);
const projectList = ref<PwlProject[]>([]);
const xlsFileName = ref<string>();
// 是否显示用户导入弹窗
const showImport = ref(false);
// 表单数据
const {where,resetFields} = useSearch<PwlProjectParam>({
id: undefined,
type: undefined,
itemYear: undefined,
keywords: undefined
});
/* 打开编辑弹窗 */
const openImport = () => {
showImport.value = true;
};
const chooseType = (e) => {
console.log(e,'yyyy')
where.type = e.label;
search();
}
// 批量删除
const removeBatch = () => {
emit('remove');
};
const onYear = (date: any, dateString: string) => {
where.itemYear = dateString;
search();
};
/* 搜索 */
const search = () => {
const [d1, d2] = dateRange.value ?? [];
emit('search', {
...where,
createTimeStart: d1 ? d1 + ' 00:00:00' : '',
createTimeEnd: d2 ? d2 + ' 23:59:59' : ''
});
};
/* 重置 */
const reset = () => {
resetFields();
search();
};
// 导出
const handleExport = async () => {
loading.value = true;
const array: (string | number)[][] = [
[
'报告时间',
'审计单位',
'报告编号',
'项目信息-开票单位/汇款人',
'项目信息-所属年度',
'项目信息-类型',
'项目信息-审计意见',
'年末资产总额(万元)',
'合同金额',
'实收金额',
'到账信息-银行',
'到账信息-日期',
'到账信息-金额',
'开票信息-日期',
'开票信息-金额',
'开票信息-发票类型',
'报告份数',
'底稿人员',
'参与人员',
'签字注会',
'展业人员',
'底稿情况'
]
];
// 按搜索结果导出
where.sceneType = 'Content';
await listPwlProject(where)
.then((list) => {
projectList.value = list;
list?.forEach((d: PwlProject) => {
array.push([
`${d.expirationTime || ''}`,
`${d.name || ''}`,
`${d.code || ''}`,
`${d.itemName || ''}`,
`${d.itemYear || ''}`,
`${d.itemType || ''}`,
`${d.itemOpinion || ''}`,
`${d.totalAssets || ''}`,
// `${d.comments || ''}`,
`${d.contractPrice || ''}`,
`${d.payPrice || ''}`,
`${d.bankName || ''}`,
`${d.bankPayTime || ''}`,
`${d.bankPrice || ''}`,
`${d.invoiceTime || ''}`,
`${d.invoicePrice || ''}`,
`${d.invoiceType || ''}`,
`${d.reportNum || ''}`,
`${d.draftUser ? JSON.parse(d.draftUser).join(',') : ''}`,
`${d.users ? JSON.parse(d.users).join(',') : ''}`,
`${d.signUser ? JSON.parse(d.signUser).join(',') : ''}`,
`${d.saleUser ? JSON.parse(d.saleUser).join(',') : ''}`,
`${d.files || ''}`,
]);
});
const sheetName = `导出项目列表${dayjs(new Date()).format('YYYYMMDD')}`;
const workbook = {
SheetNames: [sheetName],
Sheets: {}
};
const sheet = utils.aoa_to_sheet(array);
workbook.Sheets[sheetName] = sheet;
// 设置列宽
sheet['!cols'] = [
];
message.loading('正在导出...');
setTimeout(() => {
writeFile(
workbook,
`${
where.createTimeEnd ? xlsFileName.value + '_' : ''
}${sheetName}.xlsx`
);
loading.value = false;
}, 1000);
})
.catch((msg) => {
message.error(msg);
loading.value = false;
})
.finally(() => {
});
};
watch( watch(
() => props.selection, () => props.selection,
() => {} () => {
}
); );
</script> </script>

View File

@@ -1,15 +1,19 @@
<template> <template>
<div class="page"> <a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<div class="ele-body"> <template #extra>
<Extra/>
</template>
<a-card :bordered="false" :body-style="{ padding: '16px' }"> <a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table <ele-pro-table
ref="tableRef" ref="tableRef"
row-key="pwlProjectId" row-key="id"
:columns="columns" :columns="columns"
:datasource="datasource" :datasource="datasource"
:customRow="customRow" :customRow="customRow"
:scroll="{ x: 4000 }"
tool-class="ele-toolbar-form" tool-class="ele-toolbar-form"
class="sys-org-table" class="sys-org-table"
bordered
> >
<template #toolbar> <template #toolbar>
<search <search
@@ -22,11 +26,47 @@
</template> </template>
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'"> <template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" /> <a-image v-if="record.image" :src="record.image" :width="50"/>
</template> </template>
<template v-if="column.key === 'status'"> <template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag> <a-tag v-if="record.status === 0" color="green">已完成</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag> <a-tag v-if="record.status === 1" color="red">未完成</a-tag>
</template>
<template v-if="column.key === 'draftUser'">
<a-space direction="vertical" v-if="record.draftUser">
<a-tag v-for="(item,index) in JSON.parse(record.draftUser)" :key="index">{{ item }}</a-tag>
</a-space>
</template>
<template v-if="column.key === 'users'">
<a-space direction="vertical" v-if="record.users">
<a-tag v-for="(item,index) in JSON.parse(record.users)" :key="index">{{ item }}</a-tag>
</a-space>
</template>
<template v-if="column.key === 'signUser'">
<a-space direction="vertical" v-if="record.signUser">
<a-tag v-for="(item,index) in JSON.parse(record.signUser)" :key="index">{{ item }}</a-tag>
</a-space>
</template>
<template v-if="column.key === 'saleUser'">
<a-space direction="vertical" v-if="record.saleUser">
<a-tag v-for="(item,index) in JSON.parse(record.saleUser)" :key="index">{{ item }}</a-tag>
</a-space>
</template>
<template v-if="column.key === 'electron'">
<span>电子</span>
<a-tag v-if="record.electron === 0" color="green">已完成</a-tag>
<a-tag v-else color="red">未完成</a-tag>
<span>纸质</span>
<a-tag v-if="record.paper === 0" color="green">已完成</a-tag>
<a-tag v-else color="red">未完成</a-tag>
</template>
<template v-if="column.key === 'createTime'">
<a-tooltip :title="`创建于:${record.createTime}`" class="flex flex-col">
<a-space>
<span>{{ record.createTime }}</span>
<a-avatar :src="record.avatar" size="small" />
</a-space>
</a-tooltip>
</template> </template>
<template v-if="column.key === 'action'"> <template v-if="column.key === 'action'">
<a-space> <a-space>
@@ -46,8 +86,7 @@
<!-- 编辑弹窗 --> <!-- 编辑弹窗 -->
<PwlProjectEdit v-model:visible="showEdit" :data="current" @done="reload"/> <PwlProjectEdit v-model:visible="showEdit" :data="current" @done="reload"/>
</div> </a-page-header>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@@ -64,6 +103,8 @@
import PwlProjectEdit from './components/pwlProjectEdit.vue'; import PwlProjectEdit from './components/pwlProjectEdit.vue';
import {pagePwlProject, removePwlProject, removeBatchPwlProject} from '@/api/pwl/pwlProject'; import {pagePwlProject, removePwlProject, removeBatchPwlProject} from '@/api/pwl/pwlProject';
import type {PwlProject, PwlProjectParam} from '@/api/pwl/pwlProject/model'; import type {PwlProject, PwlProjectParam} from '@/api/pwl/pwlProject/model';
import {getPageTitle} from "@/utils/common";
import Extra from "./components/extra.vue";
// 表格实例 // 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null); const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
@@ -76,6 +117,10 @@
const showEdit = ref(false); const showEdit = ref(false);
// 是否显示批量移动弹窗 // 是否显示批量移动弹窗
const showMove = ref(false); const showMove = ref(false);
// const draftUser = ref<string[]>([]);
// const users = ref<string[]>([]);
// const signUser = ref<string[]>([]);
// const saleUser = ref<string[]>([]);
// 加载状态 // 加载状态
const loading = ref(true); const loading = ref(true);
@@ -84,12 +129,9 @@
page, page,
limit, limit,
where, where,
orders, orders
filters
}) => { }) => {
if (filters) {
where.status = filters.status;
}
return pagePwlProject({ return pagePwlProject({
...where, ...where,
...orders, ...orders,
@@ -101,77 +143,89 @@
// 表格列配置 // 表格列配置
const columns = ref<ColumnItem[]>([ const columns = ref<ColumnItem[]>([
{ {
title: 'ID', title: '序号',
dataIndex: 'id', key: 'index',
key: 'id', width: 48,
fixed: 'left',
align: 'center', align: 'center',
width: 90, customRender: ({ index }) => index + (tableRef.value?.tableIndex ?? 0)
}, },
{ {
title: '审计单位', title: '审计单位',
dataIndex: 'name', dataIndex: 'name',
key: 'name', key: 'name',
width: 240,
fixed: 'left',
align: 'center', align: 'center',
}, },
{ {
title: '报告编号', title: '报告编号',
dataIndex: 'code', dataIndex: 'code',
key: 'code', key: 'code',
width: 240,
sorter: true,
align: 'center', align: 'center',
}, },
{ {
title: '上级id, 0是顶级', title: '项目完成进度',
dataIndex: 'parentId', dataIndex: 'status',
key: 'parentId', key: 'status',
width: 120,
align: 'center'
},
{
title: '报告时间',
dataIndex: 'expirationTime',
key: 'expirationTime',
align: 'center',
width: 120
},
// {
// title: '类型',
// dataIndex: 'type',
// key: 'type',
// align: 'center',
// customRender: ({text}) => ['审字', '专审', '验证', '咨询'][text]
// },
{
title: '项目信息',
dataIndex: 'itemName',
key: 'itemName',
align: 'center',
children: [
{
title: '开票单位/汇款人',
dataIndex: 'itemName',
key: 'itemName',
align: 'center',
width: 180
},
{
title: '所属年度',
dataIndex: 'itemYear',
key: 'itemYear',
align: 'center', align: 'center',
}, },
{ {
title: '项目类型', title: '类型',
dataIndex: 'type', dataIndex: 'itemType',
key: 'type', key: 'itemType',
align: 'center', align: 'center',
}, },
{ {
title: '项目图标', title: '审计意见',
dataIndex: 'image', dataIndex: 'itemOpinion',
key: 'image', key: 'itemOpinion',
align: 'center', align: 'center',
}, },
{ ]
title: '二维码',
dataIndex: 'qrcode',
key: 'qrcode',
align: 'center',
},
{
title: '链接地址',
dataIndex: 'url',
key: 'url',
align: 'center',
},
{
title: '应用截图',
dataIndex: 'images',
key: 'images',
align: 'center',
},
{
title: '底稿情况',
dataIndex: 'files',
key: 'files',
align: 'center',
},
{
title: '应用介绍',
dataIndex: 'content',
key: 'content',
align: 'center',
}, },
{ {
title: '年末资产总额(万元)', title: '年末资产总额(万元)',
dataIndex: 'totalAssets', dataIndex: 'totalAssets',
key: 'totalAssets', key: 'totalAssets',
align: 'center', align: 'center',
width: 170
}, },
{ {
title: '合同金额', title: '合同金额',
@@ -184,180 +238,115 @@
dataIndex: 'payPrice', dataIndex: 'payPrice',
key: 'payPrice', key: 'payPrice',
align: 'center', align: 'center',
width: 120
}, },
{ {
title: '软件定价', title: '到账信息',
dataIndex: 'price',
key: 'price',
align: 'center',
},
{
title: '是否推荐',
dataIndex: 'recommend',
key: 'recommend',
align: 'center',
},
{
title: '到期时间',
dataIndex: 'expirationTime',
key: 'expirationTime',
align: 'center',
},
{
title: '项目信息-开票单位/汇款人',
dataIndex: 'itemName', dataIndex: 'itemName',
key: 'itemName', key: 'itemName',
align: 'center', align: 'center',
}, children: [
{ {
title: '项目信息-年度', title: '银行',
dataIndex: 'itemYear',
key: 'itemYear',
align: 'center',
},
{
title: '项目信息-类型',
dataIndex: 'itemType',
key: 'itemType',
align: 'center',
},
{
title: '项目信息-审计意见',
dataIndex: 'itemOpinion',
key: 'itemOpinion',
align: 'center',
},
{
title: '到账信息-银行名称',
dataIndex: 'bankName', dataIndex: 'bankName',
key: 'bankName', key: 'bankName',
align: 'center', align: 'center',
width: 120,
}, },
{ {
title: '到账日期', title: '日期',
dataIndex: 'bankPayTime', dataIndex: 'bankPayTime',
key: 'bankPayTime', key: 'bankPayTime',
align: 'center', align: 'center',
width: 120
}, },
{ {
title: '到账金额', title: '金额',
dataIndex: 'bankPrice', dataIndex: 'bankPrice',
key: 'bankPrice', key: 'bankPrice',
align: 'center', align: 'center',
width: 120,
},
],
}, },
{ {
title: '发票类型', title: '开票信息',
dataIndex: 'invoiceType', dataIndex: 'invoice',
key: 'invoiceType', key: 'invoice',
align: 'center', align: 'center',
}, children: [
{ {
title: '开票日期', title: '日期',
dataIndex: 'invoiceTime', dataIndex: 'invoiceTime',
key: 'invoiceTime', key: 'invoiceTime',
align: 'center', align: 'center',
width: 120
}, },
{ {
title: '开票金额', title: '金额',
dataIndex: 'invoicePrice', dataIndex: 'invoicePrice',
key: 'invoicePrice', key: 'invoicePrice',
align: 'center', align: 'center',
width: 120,
}, },
{
title: '发票类型',
dataIndex: 'invoiceTypeName',
key: 'invoiceTypeName',
align: 'center',
width: 120,
},
],
},
{ {
title: '报告份数', title: '报告份数',
dataIndex: 'reportNum', dataIndex: 'reportNum',
key: 'reportNum', key: 'reportNum',
align: 'center', align: 'center',
}, width: 90,
{
title: '底稿人员',
dataIndex: 'draftUserId',
key: 'draftUserId',
align: 'center',
}, },
{ {
title: '底稿人员', title: '底稿人员',
dataIndex: 'draftUser', dataIndex: 'draftUser',
key: 'draftUser', key: 'draftUser',
align: 'center', align: 'center',
}, width: 90
{
title: '参与成员',
dataIndex: 'userIds',
key: 'userIds',
align: 'center',
}, },
{ {
title: '参与成员', title: '参与成员',
dataIndex: 'users', dataIndex: 'users',
key: 'users', key: 'users',
align: 'center', align: 'center',
}, width: 180
{
title: '签字注会',
dataIndex: 'signUserId',
key: 'signUserId',
align: 'center',
}, },
{ {
title: '签字注会', title: '签字注会',
dataIndex: 'signUser', dataIndex: 'signUser',
key: 'signUser', key: 'signUser',
align: 'center', align: 'center',
}, width: 90,
{
title: '展业人员',
dataIndex: 'saleUserId',
key: 'saleUserId',
align: 'center',
}, },
{ {
title: '展业人员', title: '展业人员',
dataIndex: 'saleUser', dataIndex: 'saleUser',
key: 'saleUser', key: 'saleUser',
align: 'center', align: 'center',
width: 90
}, },
{ {
title: '纸质底稿完成情况', title: '底稿完成情况',
dataIndex: 'paper',
key: 'paper',
align: 'center',
},
{
title: '电子底稿完成情况',
dataIndex: 'electron', dataIndex: 'electron',
key: 'electron', key: 'electron',
align: 'center', align: 'center',
width: 120,
}, },
{ {
title: '备注', title: '备注',
dataIndex: 'comments', dataIndex: 'comments',
key: 'comments', key: 'comments',
align: 'center', align: 'center',
}, width: 180,
{
title: '排序(数字越小越靠前)',
dataIndex: 'sortNumber',
key: 'sortNumber',
align: 'center',
},
{
title: '状态, 0正常, 1冻结',
dataIndex: 'status',
key: 'status',
align: 'center',
},
{
title: '是否删除, 0否, 1是',
dataIndex: 'deleted',
key: 'deleted',
align: 'center',
},
{
title: '客户ID',
dataIndex: 'userId',
key: 'userId',
align: 'center',
}, },
{ {
title: '创建时间', title: '创建时间',
@@ -365,20 +354,9 @@
key: 'createTime', key: 'createTime',
align: 'center', align: 'center',
sorter: true, sorter: true,
width: 180,
ellipsis: true, ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd') customRender: ({text}) => toDateString(text, 'yyyy-MM-dd HH:mm:ss')
},
{
title: '修改时间',
dataIndex: 'updateTime',
key: 'updateTime',
align: 'center',
},
{
title: '知识库ID',
dataIndex: 'kbId',
key: 'kbId',
align: 'center',
}, },
{ {
title: '操作', title: '操作',
@@ -410,7 +388,7 @@
/* 删除单个 */ /* 删除单个 */
const remove = (row: PwlProject) => { const remove = (row: PwlProject) => {
const hide = message.loading('请求中..', 0); const hide = message.loading('请求中..', 0);
removePwlProject(row.pwlProjectId) removePwlProject(row.id)
.then((msg) => { .then((msg) => {
hide(); hide();
message.success(msg); message.success(msg);
@@ -435,7 +413,7 @@
maskClosable: true, maskClosable: true,
onOk: () => { onOk: () => {
const hide = message.loading('请求中..', 0); const hide = message.loading('请求中..', 0);
removeBatchPwlProject(selection.value.map((d) => d.pwlProjectId)) removeBatchPwlProject(selection.value.map((d) => d.id))
.then((msg) => { .then((msg) => {
hide(); hide();
message.success(msg); message.success(msg);

View File

@@ -94,6 +94,7 @@
import 'bytemd/dist/index.min.css'; import 'bytemd/dist/index.min.css';
import highlight from '@bytemd/plugin-highlight-ssr'; import highlight from '@bytemd/plugin-highlight-ssr';
import 'highlight.js/styles/default.css'; import 'highlight.js/styles/default.css';
import { ShopMerchantAccount } from '@/api/shop/shopMerchantAccount/model';
import { User } from '@/api/system/user/model'; import { User } from '@/api/system/user/model';
// 是否是修改 // 是否是修改
@@ -123,7 +124,7 @@
// 表格选中数据 // 表格选中数据
const formRef = ref<FormInstance | null>(null); const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]); const images = ref<ItemType[]>([]);
const merchantAccount = ref<any[]>([]); const merchantAccount = ref<ShopMerchantAccount[]>([]);
const formDataBatch = ref<ChatMessage[]>([]); const formDataBatch = ref<ChatMessage[]>([]);
// 用户信息 // 用户信息