新增历史结算;

新增租单管理
This commit is contained in:
2023-06-13 01:44:18 +08:00
parent df95f7d02c
commit 14cbb87311
20 changed files with 2301 additions and 48 deletions

View File

@@ -35,6 +35,9 @@ export interface Contract {
// 跨月结算日期 // 跨月结算日期
extendMonthDate?: number; extendMonthDate?: number;
userId?: any; userId?: any;
projectName?: any;
customerName?: any;
companyName?: any;
} }

View File

@@ -4,7 +4,7 @@ export interface TowerFall {
id?: number; id?: number;
code?: string; code?: string;
model?: string; model?: string;
companyId?: number; // companyId?: number;
companyName: string; companyName: string;
factory?: string; factory?: string;
factoryDate?: string; factoryDate?: string;

View File

@@ -0,0 +1,129 @@
import request from '@/utils/request';
import type {ApiResult, PageResult} from '@/api';
import type {HistorySettle, HistorySettleParam} from './model';
/**
* 分页查询结算
*/
export async function pageHistorySettle(params: HistorySettleParam) {
const res = await request.get<ApiResult<PageResult<HistorySettle>>>(
'/tower/tower-history-settle/page',
{
params
}
);
if (res.data.code === 0) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 查询合同列表
*/
export async function listHistorySettle(params?: HistorySettleParam) {
const res = await request.get<ApiResult<HistorySettle[]>>(
'/tower/tower-history-settle',
{
params
}
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 根据id查询合同
*/
export async function getHistorySettle(id: number) {
const res = await request.get<ApiResult<HistorySettle>>(
'/tower/tower-history-settle/' + id
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 添加合同
*/
export async function addHistorySettle(data: HistorySettle) {
const res = await request.post<ApiResult<unknown>>(
'/tower/tower-history-settle',
data
);
if (res.data.code === 0) {
return res.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 修改合同
*/
export async function updateHistorySettle(data: HistorySettle) {
const res = await request.put<ApiResult<unknown>>(
'/tower/tower-history-settle',
data
);
if (res.data.code === 0) {
return res.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 批量修改合同
*/
export async function updateBatchHistorySettle(data: HistorySettle[]) {
const res = await request.put<ApiResult<unknown>>(
'/tower/tower-history-settle/batch',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
export async function addBatchHistorySettle(data: HistorySettle[]) {
const res = await request.post<ApiResult<unknown>>(
'/tower/tower-history-settle/batch',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 删除合同
*/
export async function removeHistorySettle(id?: number) {
const res = await request.delete<ApiResult<unknown>>(
'/tower/tower-history-settle/' + id
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 批量删除合同
*/
export async function removeBatchHistorySettle(data: (number | undefined)[]) {
const res = await request.delete<ApiResult<unknown>>(
'/tower/tower-history-settle/batch',
{
data
}
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}

View File

@@ -0,0 +1,36 @@
import type { PageParam } from '@/api';
/**
* 合同
*/
export interface HistorySettle {
historySettleId?: any;
projectId?: any;
projectName?: any;
equipmentName?: any;
equipmentModel?: any;
factoryNo?: any;
filingNo?: any;
equipmentNo?: any;
contractNo?: any;
equipmentId?: any;
amountWithTax?: any;
taxAmount?: any;
inOutWithTax?: any;
inOutTax?: any;
workerAmountWithTax?: any;
workerTax?: any;
otherAmountWithTax?: string;
otherTax?: string;
startDate?: any;
endDate?: any;
userId?: any;
}
/**
* 合同搜索条件
*/
export interface HistorySettleParam extends PageParam {
projectId?: string;
contractNo?: string;
}

View File

@@ -0,0 +1,129 @@
import request from '@/utils/request';
import type {ApiResult, PageResult} from '@/api';
import type {RentRecord, RentRecordParam} from './model';
/**
* 分页查询结算
*/
export async function pageRentRecord(params: RentRecordParam) {
const res = await request.get<ApiResult<PageResult<RentRecord>>>(
'/tower/tower-rent-record/page',
{
params
}
);
if (res.data.code === 0) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 查询合同列表
*/
export async function listRentRecord(params?: RentRecordParam) {
const res = await request.get<ApiResult<RentRecord[]>>(
'/tower/tower-rent-record',
{
params
}
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 根据id查询合同
*/
export async function getRentRecord(id: number) {
const res = await request.get<ApiResult<RentRecord>>(
'/tower/tower-rent-record/' + id
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 添加合同
*/
export async function addRentRecord(data: RentRecord) {
const res = await request.post<ApiResult<unknown>>(
'/tower/tower-rent-record',
data
);
if (res.data.code === 0) {
return res.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 修改合同
*/
export async function updateRentRecord(data: RentRecord) {
const res = await request.put<ApiResult<unknown>>(
'/tower/tower-rent-record',
data
);
if (res.data.code === 0) {
return res.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 批量修改合同
*/
export async function updateBatchRentRecord(data: RentRecord[]) {
const res = await request.put<ApiResult<unknown>>(
'/tower/tower-rent-record/batch',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
export async function addBatchRentRecord(data: RentRecord[]) {
const res = await request.post<ApiResult<unknown>>(
'/tower/tower-rent-record/batch',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 删除合同
*/
export async function removeRentRecord(id?: number) {
const res = await request.delete<ApiResult<unknown>>(
'/tower/tower-rent-record/' + id
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 批量删除合同
*/
export async function removeBatchRentRecord(data: (number | undefined)[]) {
const res = await request.delete<ApiResult<unknown>>(
'/tower/tower-rent-record/batch',
{
data
}
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}

View File

@@ -0,0 +1,33 @@
import type { PageParam } from "@/api";
export interface RentRecord {
rentRecordId?: any;
projectId?: any;
contractNo?: any;
equipmentId?: any;
startDate?: any;
stopDate?: any;
checkDate?: any;
reportStopDate?: any;
reportStopType?: string;
restoreDate?: string;
reportMonitorNo?: any;
companyId?: any;
status?: any;
userId?: any;
projectName?: any;
companyName?: any;
equipmentName?: any;
equipmentModel?: any;
filingNo?: any;
equipmentNo?: any;
factoryNo?: any;
}
export interface RentRecordParam extends PageParam {
projectId?: string;
contractNo?: string;
status?: string;
}
export const rentRecordStatusList = ["未起租", "起租", "中途报停", "报停复工", "拆卸停租"];

View File

@@ -135,7 +135,7 @@ const customRow = (record: Contract) => {
// 行点击事件 // 行点击事件
onClick: () => { onClick: () => {
updateVisible(false); updateVisible(false);
if (!record.settleMethod) { if (record.settleMethod === null || record.settleMethod === undefined) {
message.warning("请先设置合同结算方式"); message.warning("请先设置合同结算方式");
return; return;
} }

View File

@@ -29,7 +29,7 @@
value?: string; value?: string;
placeholder?: string; placeholder?: string;
dictCode?: string; dictCode?: string;
multiple?: string; multiple?: any;
}>(), }>(),
{ {
placeholder: '请选择服务器厂商' placeholder: '请选择服务器厂商'

View File

@@ -280,6 +280,8 @@ import { TowerModel } from "@/api/tower/model/model";
import { listTowerModel } from "@/api/tower/model"; import { listTowerModel } from "@/api/tower/model";
import { addBatchContractFile, listContractFile } from "@/api/tower/contractFile"; import { addBatchContractFile, listContractFile } from "@/api/tower/contractFile";
import { addBatchContractEquipment, listContractEquipment } from "@/api/tower/contractEquipment"; import { addBatchContractEquipment, listContractEquipment } from "@/api/tower/contractEquipment";
import { Company } from "@/api/system/company/model";
import { Customer } from "@/api/oa/customer/model";
const userStore = useUserStore(); const userStore = useUserStore();
// 当前用户信息 // 当前用户信息
@@ -367,14 +369,14 @@ const chooseProject = (res) => {
form.projectId = res.projectId; form.projectId = res.projectId;
}; };
const companyName = ref<string>(""); const companyName = ref<string>();
const chooseCompany = (res) => { const chooseCompany = (res: Company) => {
companyName.value = res.companyName; companyName.value = res.companyName;
form.companyId = res.companyId; form.companyId = res.companyId;
}; };
const customerName = ref<string>(""); const customerName = ref<string>("");
const chooseCustomer = (res) => { const chooseCustomer = (res: Customer) => {
customerName.value = res.name; customerName.value = res.name;
form.customerId = res.customerId; form.customerId = res.customerId;
form.customerContact = res.contact; form.customerContact = res.contact;
@@ -399,7 +401,7 @@ const delFile = index => {
}; };
const getContractFileList = async () => { const getContractFileList = async () => {
contractFileList.value = await listContractFile({contactId: props.data?.contractId}) contractFileList.value = await listContractFile({ contactId: props.data?.contractId });
}; };
const equipmentList = ref<TowerModel[]>([]); const equipmentList = ref<TowerModel[]>([]);
@@ -409,7 +411,7 @@ const getEquipmentList = async () => {
const contractEquipmentList = ref<ContractEquipment[]>([]); const contractEquipmentList = ref<ContractEquipment[]>([]);
const getContractEquipmentList = async () => { const getContractEquipmentList = async () => {
contractEquipmentList.value = await listContractEquipment({contactId: props.data?.contractId}) contractEquipmentList.value = await listContractEquipment({ contactId: props.data?.contractId });
}; };
const chooseEquipmentName = (res, index) => { const chooseEquipmentName = (res, index) => {
@@ -455,7 +457,7 @@ const save = () => {
}; };
for (let i = 0; i < contractEquipmentList.value.length; i++) { for (let i = 0; i < contractEquipmentList.value.length; i++) {
if (!contractEquipmentList.value[i].equipmentName) { if (!contractEquipmentList.value[i].equipmentName) {
console.log(contractEquipmentList.value[i].equipmentName) console.log(contractEquipmentList.value[i].equipmentName);
loading.value = false; loading.value = false;
message.error("请选择设备"); message.error("请选择设备");
return; return;
@@ -552,6 +554,7 @@ watch(
isUpdate.value = false; isUpdate.value = false;
} }
} else { } else {
projectName.value = customerName.value = companyName.value = "";
resetFields(); resetFields();
} }
} }

View File

@@ -385,7 +385,7 @@ const getEquipmentList = async () => {
/* 保存编辑 */ /* 保存编辑 */
const save = () => { const save = () => {
if (!contract.settleMethod) { if (contract.settleMethod === undefined) {
message.error("请选择结算方式"); message.error("请选择结算方式");
return; return;
} }

View File

@@ -47,7 +47,7 @@
<a-form-item label="产权单位" name="companyId"> <a-form-item label="产权单位" name="companyId">
<DictSelect <DictSelect
:placeholder="'请选择产权单位'" :placeholder="'请选择产权单位'"
v-model:value="form.companyId" v-model:value="form.companyName"
:dict-code="'PropertyCompany'" :dict-code="'PropertyCompany'"
@done="chooseCompanyName" @done="chooseCompanyName"
/> />
@@ -158,6 +158,7 @@ import { User } from "@/api/user/model";
import { Organization } from "@/api/system/organization/model"; import { Organization } from "@/api/system/organization/model";
import { Customer } from "@/api/oa/customer/model"; import { Customer } from "@/api/oa/customer/model";
import { Company } from "@/api/system/company/model"; import { Company } from "@/api/system/company/model";
import { DictData } from "@/api/system/dict-data/model";
// 是否是修改 // 是否是修改
const isUpdate = ref(false); const isUpdate = ref(false);
const useForm = Form.useForm; const useForm = Form.useForm;
@@ -216,7 +217,7 @@ const token = localStorage.getItem(TOKEN_STORE_NAME);
const form = reactive<TowerFall>({ const form = reactive<TowerFall>({
id: undefined, id: undefined,
code: undefined, code: undefined,
companyId: undefined, companyName: '',
model: undefined, model: undefined,
factory: "", factory: "",
factoryDate: "", factoryDate: "",
@@ -334,32 +335,9 @@ const onFile4 = (info: FileInfo) => {
form.file4 = JSON.stringify(file4.value); form.file4 = JSON.stringify(file4.value);
}; };
const chooseFallName = (data) => { const chooseCompanyName = (data: DictData) => {
form.name = data.name; console.log(data)
form.model = data.model; // form.companyId = data.customerId;
};
const chooseFallModel = (data) => {
form.name = data.name;
form.model = data.model;
};
const chooseWarehouse = (data: Warehouse) => {
console.log(data);
form.warehouse = data.warehouseName;
form.currentLocation = data.address;
};
const chooseUsers = (data: User) => {
form.users = data.nickname;
};
const chooseOrganization = (data: Organization) => {
form.organization = data.organizationName;
};
const chooseCompanyName = (data: Customer) => {
form.company = data.customerName;
}; };
/* 保存编辑 */ /* 保存编辑 */
@@ -409,15 +387,6 @@ watch(
if (props.data.file4) { if (props.data.file4) {
file4.value = JSON.parse(props.data.file4); file4.value = JSON.parse(props.data.file4);
} }
if (props.data.file5) {
file5.value = JSON.parse(props.data.file5);
}
if (props.data.file6) {
file6.value = JSON.parse(props.data.file6);
}
if (props.data.file7) {
file7.value = JSON.parse(props.data.file7);
}
isUpdate.value = true; isUpdate.value = true;
} else { } else {
isUpdate.value = false; isUpdate.value = false;

View File

@@ -5,7 +5,7 @@
<!-- 表格 --> <!-- 表格 -->
<ele-pro-table <ele-pro-table
ref="tableRef" ref="tableRef"
row-key="fallId" row-key="id"
:columns="columns" :columns="columns"
:datasource="datasource" :datasource="datasource"
v-model:selection="selection" v-model:selection="selection"

View File

@@ -0,0 +1,342 @@
<!-- 用户编辑弹窗 -->
<template>
<ele-modal
:width="'90%'"
:visible="visible"
:confirm-loading="loading"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑结算' : '添加结算'"
:body-style="{ paddingBottom: '8px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
:label-col="{ md: { span: 6 }, sm: { span: 20 }, xs: { span: 24 } }"
:wrapper-col="{ md: { span: 24 }, sm: { span: 20 }, xs: { span: 24 } }"
>
<a-card title="填报人信息" :bordered="false">
<a-row :gutter="16">
<a-col :md="12" :sm="24" :xs="24">
<a-form-item label="填报人">
<a-input disabled v-model:value="loginUser.username" />
</a-form-item>
</a-col>
<a-col :md="12" :sm="24" :xs="24">
<a-form-item label="填报时间">
<a-input disabled v-model:value="now" />
</a-form-item>
</a-col>
</a-row>
</a-card>
<a-card title="设备信息" :bordered="false">
<a-row :gutter="16">
<a-col :md="8" :sm="24" :xs="24">
<a-form-item label="项目名称" v-bind="validateInfos.projectId">
<ProjectSelectModel
:placeholder="`请选择项目`"
v-model:value="form.projectName"
@done="chooseProject"
/>
</a-form-item>
<a-form-item label="自编号">
<p style="margin: 0">{{ form.equipmentNo }}</p>
</a-form-item>
</a-col>
<a-col :md="8" :sm="24" :xs="24">
<a-form-item label="合同编号" v-bind="validateInfos.contractNo">
<a-input v-model:value="form.contractNo" />
</a-form-item>
<a-form-item label="出场编号">
<p style="margin: 0">{{ form.factoryNo }}</p>
</a-form-item>
</a-col>
<a-col :md="8" :sm="24" :xs="24">
<a-form-item label="设备名称">
<TowerEquipmentModel
:placeholder="`请选择设备名称`"
v-model:value="form.equipmentName"
@done="chooseEquipmentName"
/>
</a-form-item>
<a-form-item label="备案编号">
<p style="margin: 0">{{ form.filingNo }}</p>
</a-form-item>
</a-col>
</a-row>
</a-card>
<a-card title="结算信息" :bordered="false">
<a-row :gutter="16">
<a-col :md="8" :sm="24" :xs="24">
<a-form-item label="设备结算租金含税金额" v-bind="validateInfos.amountWithTax">
<a-input v-model:value="form.amountWithTax" placeholder="设备结算租金含税金额">
<template #suffix></template>
</a-input>
</a-form-item>
<a-form-item label="进出场费税率" v-bind="validateInfos.inOutTax">
<a-input v-model:value="form.inOutTax" placeholder="进出场费税率">
<template #suffix>%</template>
</a-input>
</a-form-item>
<a-form-item label="其他费用含税金额" v-bind="validateInfos.otherAmountWithTax">
<a-input v-model:value="form.otherAmountWithTax" placeholder="其他费用含税金额">
<template #suffix></template>
</a-input>
</a-form-item>
<a-form-item label="结束日期">
<a-date-picker
class="ele-fluid"
placeholder="请选择结束日期"
v-model:value="form.endDate"
valueFormat="YYYY-MM-DD"
/>
</a-form-item>
</a-col>
<a-col :md="8" :sm="24" :xs="24">
<a-form-item label="设备结算租金税率" v-bind="validateInfos.taxAmount">
<a-input v-model:value="form.taxAmount" placeholder="设备结算租金税率">
<template #suffix>%</template>
</a-input>
</a-form-item>
<a-form-item label="劳务费含税金额" v-bind="validateInfos.workerAmountWithTax">
<a-input v-model:value="form.workerAmountWithTax" placeholder="劳务费含税金额">
<template #suffix></template>
</a-input>
</a-form-item>
<a-form-item label="其他费用税率" v-bind="validateInfos.otherTax">
<a-input v-model:value="form.otherTax" placeholder="其他费用税率">
<template #suffix></template>
</a-input>
</a-form-item>
</a-col>
<a-col :md="8" :sm="24" :xs="24">
<a-form-item label="进出场费含税金额" v-bind="validateInfos.inOutWithTax">
<a-input v-model:value="form.inOutWithTax" placeholder="进出场费含税金额">
<template #suffix></template>
</a-input>
</a-form-item>
<a-form-item label="劳务费税率" v-bind="validateInfos.workerTax">
<a-input v-model:value="form.workerTax" placeholder="劳务费税率">
<template #suffix>%</template>
</a-input>
</a-form-item>
<a-form-item label="开始日期">
<a-date-picker
class="ele-fluid"
placeholder="请选择开始日期"
v-model:value="form.startDate"
valueFormat="YYYY-MM-DD"
/>
</a-form-item>
</a-col>
</a-row>
</a-card>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch, computed } from "vue";
import { Form, message } from "ant-design-vue";
import { assignObject, EleProTable } from "ele-admin-pro";
import { useUserStore } from "@/store/modules/user";
import dayjs from "dayjs";
import { HistorySettle } from "@/api/tower/historySettle/model";
import { addHistorySettle, updateHistorySettle } from "@/api/tower/historySettle";
const userStore = useUserStore();
// 当前用户信息
const loginUser = computed(() => userStore.info ?? {});
const now = dayjs().format("YYYY-MM-DD HH:mm:ss");
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: HistorySettle | null;
}>();
const emit = defineEmits<{
(e: "done"): void;
(e: "update:visible", visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
const activeKey = ref(0);
const chooseProject = (res) => {
form.projectName = res.projectName;
form.projectId = res.projectId;
};
const chooseEquipmentName = (res) => {
form.equipmentId = res.equipmentId;
form.equipmentNo = res.equipmentNo;
form.equipmentName = res.name;
form.filingNo = res.filingNo;
form.equipmentModel = res.model;
form.factoryNo = res.factoryNo;
};
// 用户信息
const form = reactive<HistorySettle>({
historySettleId: "",
projectId: "",
projectName: "",
equipmentName: "",
equipmentModel: "",
factoryNo: "",
filingNo: "",
equipmentNo: "",
contractNo: "",
equipmentId: "",
amountWithTax: "",
taxAmount: "",
inOutWithTax: "",
inOutTax: "",
workerAmountWithTax: "",
workerTax: "",
otherAmountWithTax: "",
otherTax: "",
startDate: "",
endDate: "",
userId: loginUser.value.userId
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit("update:visible", value);
};
// 表单验证规则
const rules = reactive({
projectId: [
{
required: true,
message: "请选择项目",
trigger: "blur"
}
],
contractNo: [
{
required: true,
message: "请输入合同编号",
trigger: "blur"
}
],
amountWithTax: [
{
required: true,
message: "请输入设备结算租金含税金额",
trigger: "blur"
}
],
inOutTax: [
{
required: true,
message: "请输入进出场费税率",
trigger: "blur"
}
],
otherAmountWithTax: [
{
required: true,
message: "请输入其他费用含税金额",
trigger: "blur"
}
],
taxAmount: [
{
required: true,
message: "请输入设备结算租金税率",
trigger: "blur"
}
],
workerAmountWithTax: [
{
required: true,
message: "请输入劳务费含税金额",
trigger: "blur"
}
],
otherTax: [
{
required: true,
message: "请输入其他费用税率",
trigger: "blur"
}
],
inOutWithTax: [
{
required: true,
message: "请输入进出场费含税金额",
trigger: "blur"
}
],
workerTax: [
{
required: true,
message: "请输入劳务费税率",
trigger: "blur"
}
],
});
const { resetFields, validate, validateInfos } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
validate()
.then(async () => {
loading.value = true;
const data = {
...form
};
const saveOrUpdate = isUpdate.value ? updateHistorySettle : addHistorySettle;
const res = await saveOrUpdate(data).catch((e) => {
loading.value = false;
message.error(e.message);
});
message.success(res?.message);
updateVisible(false);
emit("done");
})
.catch(() => {
});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
if (props.data) {
loading.value = false;
assignObject(form, props.data);
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
}
);
</script>
<style lang="less">
.title {
font-weight: bold;
font-size: 1.1rem;
padding-bottom: 10px;
border-bottom: 1px solid #dcdfe6;
color: #494949;
margin-bottom: 10px;
}
</style>

View File

@@ -0,0 +1,148 @@
<!-- 用户编辑弹窗 -->
<template>
<ele-modal
width="75%"
:visible="visible"
:confirm-loading="loading"
:title="'合同详情'"
:maxable="true"
:body-style="{ paddingBottom: '8px' }"
@update:visible="updateVisible"
:footer="null"
>
<a-form
:label-col="{ md: { span: 4 }, sm: { span: 24 } }"
:wrapper-col="{ md: { span: 19 }, sm: { span: 24 } }"
>
<div class="base-form" style="margin-bottom: 20px">
<a-descriptions bordered>
<a-descriptions-item label="合同名称">
{{ customer.customerName }}
</a-descriptions-item>
<a-descriptions-item label="社会统一信用代码">
{{ customer.customerCode }}
</a-descriptions-item>
<a-descriptions-item label="跟进状态">
<div color="blue" v-for="(d, index) in progress" :key="index">
<span v-if="d.value == customer.progress">{{ d.label }}</span>
</div>
</a-descriptions-item>
<a-descriptions-item label="联系人">
{{ customer.customerContacts }}
</a-descriptions-item>
<a-descriptions-item label="联系电话">
{{ customer.customerMobile }}
</a-descriptions-item>
<a-descriptions-item label="座机电话">
{{ customer.customerPhone }}
</a-descriptions-item>
<a-descriptions-item label="合同类型">
<div color="blue" v-for="(d, index) in customerType" :key="index">
<span v-if="d.value == customer.customerType">{{ d.value }}</span>
</div>
</a-descriptions-item>
<a-descriptions-item label="联系地址">
{{ customer.customerAddress }}
</a-descriptions-item>
<a-descriptions-item label="备注">
{{ customer.comments }}
</a-descriptions-item>
</a-descriptions>
<!-- <a-descriptions-->
<!-- title="其他信息"-->
<!-- :column="1"-->
<!-- bordered-->
<!-- style="margin-top: 30px"-->
<!-- >-->
<!-- <a-descriptions-item label="相关项目">-->
<!-- {{ customer.comments }}-->
<!-- </a-descriptions-item>-->
<!-- </a-descriptions>-->
</div>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form } from 'ant-design-vue';
import { assignObject } from 'ele-admin-pro';
import type { Customer } from '@/api/oa/customer/model';
import { FILE_SERVER } from '@/config/setting';
import { getDictionaryOptions } from '@/utils/common';
const useForm = Form.useForm;
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: Customer | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 用户信息
const customer = reactive<Customer>({
customerCode: '',
customerName: '',
customerType: undefined,
customerMobile: '',
customerAvatar: '',
customerPhone: '',
customerContacts: '',
customerAddress: '',
comments: '',
progress: '',
status: '0',
sortNumber: 100,
customerId: 0
});
// 请求状态
const loading = ref(true);
const { resetFields } = useForm(customer);
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
/* 打开外部链接 */
// const openUrl = (record) => {
// window.open(record.panel);
// };
/* 获取字典数据 */
const customerType = getDictionaryOptions('customerType');
const progress = getDictionaryOptions('customerFollowStatus');
watch(
() => props.visible,
(visible) => {
if (visible) {
if (props.data) {
loading.value = false;
assignObject(customer, props.data);
}
} else {
resetFields();
}
}
);
</script>
<style lang="less">
.tab-pane {
min-height: 100px;
}
.card-head {
display: flex;
height: 40px;
align-items: center;
margin-bottom: 30px;
}
</style>

View File

@@ -0,0 +1,205 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>新增</span>
</a-button>
<a-button
danger
type="primary"
class="ele-btn-icon"
v-if="selection.length > 0"
@click="removeBatch"
>
<template #icon>
<DeleteOutlined />
</template>
<span>批量删除</span>
</a-button>
<!-- <a-button @click="batchMove" v-if="selection.length > 0">-->
<!-- <template #icon>-->
<!-- <UserSwitchOutlined />-->
<!-- </template>-->
<!-- 批量转移-->
<!-- </a-button>-->
<a-input-search
allow-clear
placeholder="请输入关键词"
v-model:value="searchText"
@pressEnter="search"
@search="search"
/>
</a-space>
</template>
<script lang="ts" setup>
import {
PlusOutlined,
DeleteOutlined,
UploadOutlined,
DownloadOutlined
} from '@ant-design/icons-vue';
import useSearch from '@/utils/use-search';
import type { CustomerParam } from '@/api/oa/customer/model';
import { ref, watch } from 'vue';
import { utils, read } from 'xlsx';
// import { assignObject } from 'ele-admin-pro';
import { message } from 'ant-design-vue/es';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: CustomerParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 表单数据
const { where } = useSearch<CustomerParam>({
name: '',
creditCode: '',
keywords: '',
userId: undefined
});
// 下来选项
// 搜索内容
const searchText = ref('');
/* 搜索 */
const search = () => {
where.keywords = searchText.value;
emit('search', where);
};
// 新增
const add = () => {
emit('add');
};
// 导入数据的列
const importTitle = ref<string[]>(['A', 'B', 'C', 'D', 'E', 'F', 'G']);
// 导入的数据
const importData = ref<Record<string, any>[]>([]);
// 导入数据二维数组形式
const importDataAoa = ref<(string | number)[][]>([]);
/* 导入本地 excel 文件 */
const importBatch = (file: File) => {
if (
![
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
].includes(file.type)
) {
message.error('只能选择 excel 文件');
return false;
}
if (file.size / 1024 / 1024 > 20) {
message.error('大小不能超过 20MB');
return false;
}
const reader = new FileReader();
reader.onload = (e) => {
const data = new Uint8Array(e.target?.result as any);
const workbook = read(data, { type: 'array' });
const sheetNames = workbook.SheetNames;
const worksheet = workbook.Sheets[sheetNames[0]];
// 解析成二维数组
const aoa = utils.sheet_to_json<string[]>(worksheet, { header: 1 });
// 生成表格需要的数据
let list: Record<string, any>[] = [];
let maxCols = 0;
let title: string[] = [];
aoa.forEach((d) => {
if (d.length > maxCols) {
maxCols = d.length;
}
const row = {};
for (let i = 0; i < d.length; i++) {
const key = getCharByIndex(i);
row[key] = d[i];
row['__colspan__' + key] = 1;
row['__rowspan__' + key] = 1;
}
list.push(row);
});
for (let i = 0; i < maxCols; i++) {
title.push(getCharByIndex(i));
}
importTitle.value = title;
importData.value = list;
importDataAoa.value = aoa;
};
console.log(importData.value);
console.log(importDataAoa.value);
reader.readAsArrayBuffer(file);
return false;
};
/* 生成Excel列字母序号 */
const getCharByIndex = (index: number) => {
const chars = [
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z'
];
if (index < chars.length) {
return chars[index];
}
const n = parseInt(String(index / chars.length));
const m = index % chars.length;
return chars[n] + chars[m];
};
// 转移
// const batchMove = () => {
// emit('batchMove');
// };
// 批量删除
const removeBatch = () => {
emit('remove');
};
const onClear = () => {
where.userId = undefined;
search();
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,303 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="historySettleId"
:columns="columns"
:datasource="datasource"
v-model:selection="selection"
tool-class="ele-toolbar-form"
:scroll="{ x: 800 }"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'createTime'">
<a-tooltip :title="`${toDateString(record.createTime)}`">
{{ timeAgo(record.createTime) }}
</a-tooltip>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<Edit v-model:visible="showEdit" :data="current" @done="reload" />
<!-- 合同详情弹窗 -->
<Info v-model:visible="showInfo" :data="current" @done="reload" />
<!-- 批量转移弹窗 -->
<!-- <CustomerMove v-model:visible="showMove" :data="selection" @done="batchMove" />-->
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import {
ExclamationCircleOutlined,
UserOutlined
} from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import { toDateString } from 'ele-admin-pro';
import Search from './components/search.vue';
import Edit from './components/edit.vue';
import Info from './components/info.vue';
import { timeAgo } from 'ele-admin-pro';
import { useUserStore } from '@/store/modules/user';
import { pageHistorySettle, removeBatchHistorySettle, removeHistorySettle } from "@/api/tower/historySettle";
import { HistorySettle, HistorySettleParam } from "@/api/tower/historySettle/model";
const userStore = useUserStore();
// 当前用户信息
const loginUser = computed(() => userStore.info ?? {});
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<HistorySettle[]>([]);
// 当前编辑数据
const current = ref<HistorySettle | null>(null);
// 是否显示资产详情
const showInfo = ref(false);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageHistorySettle({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
key: 'index',
width: 48,
align: 'center',
fixed: 'left',
hideInSetting: true,
customRender: ({ index }) => index + (tableRef.value?.tableIndex ?? 0)
},
{
title: '项目名称',
dataIndex: 'projectName',
key: 'projectName',
},
{
title: '合同编号',
dataIndex: 'contractNo'
},
{
title: '设备名称',
dataIndex: 'equipmentName',
},
{
title: '设备型号',
dataIndex: 'equipmentModel'
},
{
title: '出厂编号',
dataIndex: 'factoryNo',
},
{
title: '备案编号',
dataIndex: 'filingNo'
},
{
title: '自编号',
dataIndex: 'equipmentNo'
},
{
title: '设备结算租金含税金额',
dataIndex: 'amountWithTax'
},
{
title: '设备结算租金税率',
dataIndex: 'taxAmount'
},
{
title: '进出场费含税金额',
dataIndex: 'inOutWithTax'
},
{
title: '进出场费税率',
dataIndex: 'inOutTax'
},
{
title: '劳务费含税金额',
dataIndex: 'workerAmountWithTax'
},
{
title: '劳务费税率',
dataIndex: 'workerTax'
},
{
title: '其他费用含税金额',
dataIndex: 'otherAmountWithTax'
},
{
title: '其他费用税率',
dataIndex: 'otherTax'
},
{
title: '开始日期',
dataIndex: 'startDate'
},
{
title: '结束日期',
dataIndex: 'endDate'
},
{
title: '操作',
key: 'action',
width: 200,
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: HistorySettleParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: HistorySettle) => {
current.value = row ?? null;
showEdit.value = true;
};
const showSettle = ref<Boolean>(false)
const openSettle = (row?: HistorySettle) => {
current.value = row ?? null;
showSettle.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 打开用户详情弹窗 */
const openInfo = (row?: HistorySettle) => {
current.value = row ?? null;
showInfo.value = true;
};
/* 删除单个 */
const remove = (row: HistorySettle) => {
const hide = message.loading('请求中..', 0);
removeHistorySettle(row.historySettleId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量转移 */
const batchMove = (userId) => {
console.log(userId, '批量转移0000');
console.log(selection.value);
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchHistorySettle(
selection.value.map((d) => {
if (loginUser.value.userId === d.userId) {
return d.historySettleId;
}
})
)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
</script>
<script lang="ts">
export default {
name: 'Accessory'
};
</script>
<style lang="less" scoped>
.sys-org-table :deep(.ant-table-body) {
overflow: auto !important;
overflow: overlay !important;
}
.sys-org-table :deep(.ant-table-pagination.ant-pagination) {
padding: 0 4px;
margin-bottom: 0;
}
</style>

View File

@@ -0,0 +1,294 @@
<!-- 用户编辑弹窗 -->
<template>
<ele-modal
:width="'90%'"
:visible="visible"
:confirm-loading="loading"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑结算' : '添加结算'"
:body-style="{ paddingBottom: '8px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
:label-col="{ md: { span: 6 }, sm: { span: 20 }, xs: { span: 24 } }"
:wrapper-col="{ md: { span: 24 }, sm: { span: 20 }, xs: { span: 24 } }"
>
<a-card title="填报人信息" :bordered="false">
<a-row :gutter="16">
<a-col :md="12" :sm="24" :xs="24">
<a-form-item label="填报人">
<a-input disabled v-model:value="loginUser.username" />
</a-form-item>
</a-col>
<a-col :md="12" :sm="24" :xs="24">
<a-form-item label="填报时间">
<a-input disabled v-model:value="now" />
</a-form-item>
</a-col>
</a-row>
</a-card>
<a-card title="租单信息" :bordered="false">
<a-row :gutter="16">
<a-col :md="8" :sm="24" :xs="24">
<a-form-item label="项目名称" v-bind="validateInfos.projectId">
<ProjectSelectModel
:placeholder="`请选择项目`"
v-model:value="form.projectName"
@done="chooseProject"
/>
</a-form-item>
<a-form-item label="设备名称" v-bind="validateInfos.equipmentId">
<TowerEquipmentModel
:placeholder="`请选择设备名称`"
v-model:value="form.equipmentName"
@done="chooseEquipmentName"
/>
</a-form-item>
<a-form-item label="承租单位" v-bind="validateInfos.companyId">
<SelectCompany
:placeholder="`请选择承租单位`"
v-model:value="form.companyName"
@done="chooseCompany"
/>
</a-form-item>
<a-form-item label="报监编号">
<a-input v-model:value="form.reportMonitorNo" size="small" />
</a-form-item>
</a-col>
<a-col :md="8" :sm="24" :xs="24">
<a-form-item label="起租日期">
<a-date-picker
class="ele-fluid"
placeholder="请选择起租日期"
v-model:value="form.startDate"
valueFormat="YYYY-MM-DD"
/>
</a-form-item>
<a-form-item label="停租日期">
<a-date-picker
class="ele-fluid"
placeholder="请选择停租日期"
v-model:value="form.stopDate"
valueFormat="YYYY-MM-DD"
/>
</a-form-item>
<a-form-item label="检测日期">
<a-date-picker
class="ele-fluid"
placeholder="请选择检测日期"
v-model:value="form.checkDate"
valueFormat="YYYY-MM-DD"
/>
</a-form-item>
<a-form-item label="租用状态" v-bind="validateInfos.status">
<a-select placeholder="请选择租用状态" style="width: 250px" size="small"
v-model:value="form.status">
<a-select-option v-for="(item, index) in rentRecordStatusList" :key="index" :value="index">
{{ item }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="8" :sm="24" :xs="24">
<a-form-item label="合同编号" v-bind="validateInfos.contractNo">
<a-input v-model:value="form.contractNo" size="small" />
</a-form-item>
<a-form-item label="报停日期">
<a-date-picker
class="ele-fluid"
placeholder="请选择报停日期"
v-model:value="form.reportStopDate"
valueFormat="YYYY-MM-DD"
/>
</a-form-item>
<a-form-item label="报停类型">
<DictSelect
dict-code="RentReportStopType"
placeholder="请选择报停类型"
v-model:value="form.reportStopType"
/>
</a-form-item>
<a-form-item label="复工日期">
<a-date-picker
class="ele-fluid"
placeholder="请选择复工日期"
v-model:value="form.restoreDate"
valueFormat="YYYY-MM-DD"
/>
</a-form-item>
</a-col>
</a-row>
</a-card>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch, computed } from "vue";
import { Form, message } from "ant-design-vue";
import { assignObject, EleProTable } from "ele-admin-pro";
import { useUserStore } from "@/store/modules/user";
import dayjs from "dayjs";
import { RentRecord, rentRecordStatusList } from "@/api/tower/rentRecord/model";
import { addRentRecord, updateRentRecord } from "@/api/tower/rentRecord";
import { Company } from "@/api/system/company/model";
const userStore = useUserStore();
// 当前用户信息
const loginUser = computed(() => userStore.info ?? {});
const now = dayjs().format("YYYY-MM-DD HH:mm:ss");
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: RentRecord | null;
}>();
const emit = defineEmits<{
(e: "done"): void;
(e: "update:visible", visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
const activeKey = ref(0);
const chooseProject = (res) => {
form.projectName = res.projectName;
form.projectId = res.projectId;
};
const chooseEquipmentName = (res) => {
form.equipmentId = res.equipmentId;
form.equipmentName = res.name;
};
// 用户信息
const form = reactive<RentRecord>({
rentRecordId: "",
projectId: "",
projectName: "",
contractNo: "",
equipmentId: "",
equipmentName: "",
startDate: "",
stopDate: "",
checkDate: "",
reportStopDate: "",
reportStopType: "",
restoreDate: "",
reportMonitorNo: "",
companyId: "",
companyName: "",
status: "",
userId: loginUser.value.userId
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit("update:visible", value);
};
const chooseCompany = (data: Company) => {
form.companyName = data.companyName;
form.companyId = data.companyId;
};
// 表单验证规则
const rules = reactive({
projectId: [
{
required: true,
message: "请选择项目",
trigger: "blur"
}
],
contractNo: [
{
required: true,
message: "请输入合同编号",
trigger: "blur"
}
],
equipmentId: [
{
required: true,
message: "请选择设备",
trigger: "blur"
}
],
companyId: [
{
required: true,
message: "请选择承租单位",
trigger: "blur"
}
],
status: [
{
required: true,
message: "请选择租用状态",
trigger: "blur"
}
]
});
const { resetFields, validate, validateInfos } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
validate()
.then(async () => {
loading.value = true;
const data = {
...form
};
const saveOrUpdate = isUpdate.value ? updateRentRecord : addRentRecord;
const res = await saveOrUpdate(data).catch((e) => {
loading.value = false;
message.error(e.message);
});
message.success(res?.message);
updateVisible(false);
emit("done");
})
.catch(() => {
});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
if (props.data) {
loading.value = false;
assignObject(form, props.data);
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
}
);
</script>
<style lang="less">
.title {
font-weight: bold;
font-size: 1.1rem;
padding-bottom: 10px;
border-bottom: 1px solid #dcdfe6;
color: #494949;
margin-bottom: 10px;
}
</style>

View File

@@ -0,0 +1,148 @@
<!-- 用户编辑弹窗 -->
<template>
<ele-modal
width="75%"
:visible="visible"
:confirm-loading="loading"
:title="'合同详情'"
:maxable="true"
:body-style="{ paddingBottom: '8px' }"
@update:visible="updateVisible"
:footer="null"
>
<a-form
:label-col="{ md: { span: 4 }, sm: { span: 24 } }"
:wrapper-col="{ md: { span: 19 }, sm: { span: 24 } }"
>
<div class="base-form" style="margin-bottom: 20px">
<a-descriptions bordered>
<a-descriptions-item label="合同名称">
{{ customer.customerName }}
</a-descriptions-item>
<a-descriptions-item label="社会统一信用代码">
{{ customer.customerCode }}
</a-descriptions-item>
<a-descriptions-item label="跟进状态">
<div color="blue" v-for="(d, index) in progress" :key="index">
<span v-if="d.value == customer.progress">{{ d.label }}</span>
</div>
</a-descriptions-item>
<a-descriptions-item label="联系人">
{{ customer.customerContacts }}
</a-descriptions-item>
<a-descriptions-item label="联系电话">
{{ customer.customerMobile }}
</a-descriptions-item>
<a-descriptions-item label="座机电话">
{{ customer.customerPhone }}
</a-descriptions-item>
<a-descriptions-item label="合同类型">
<div color="blue" v-for="(d, index) in customerType" :key="index">
<span v-if="d.value == customer.customerType">{{ d.value }}</span>
</div>
</a-descriptions-item>
<a-descriptions-item label="联系地址">
{{ customer.customerAddress }}
</a-descriptions-item>
<a-descriptions-item label="备注">
{{ customer.comments }}
</a-descriptions-item>
</a-descriptions>
<!-- <a-descriptions-->
<!-- title="其他信息"-->
<!-- :column="1"-->
<!-- bordered-->
<!-- style="margin-top: 30px"-->
<!-- >-->
<!-- <a-descriptions-item label="相关项目">-->
<!-- {{ customer.comments }}-->
<!-- </a-descriptions-item>-->
<!-- </a-descriptions>-->
</div>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form } from 'ant-design-vue';
import { assignObject } from 'ele-admin-pro';
import type { Customer } from '@/api/oa/customer/model';
import { FILE_SERVER } from '@/config/setting';
import { getDictionaryOptions } from '@/utils/common';
const useForm = Form.useForm;
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: Customer | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 用户信息
const customer = reactive<Customer>({
customerCode: '',
customerName: '',
customerType: undefined,
customerMobile: '',
customerAvatar: '',
customerPhone: '',
customerContacts: '',
customerAddress: '',
comments: '',
progress: '',
status: '0',
sortNumber: 100,
customerId: 0
});
// 请求状态
const loading = ref(true);
const { resetFields } = useForm(customer);
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
/* 打开外部链接 */
// const openUrl = (record) => {
// window.open(record.panel);
// };
/* 获取字典数据 */
const customerType = getDictionaryOptions('customerType');
const progress = getDictionaryOptions('customerFollowStatus');
watch(
() => props.visible,
(visible) => {
if (visible) {
if (props.data) {
loading.value = false;
assignObject(customer, props.data);
}
} else {
resetFields();
}
}
);
</script>
<style lang="less">
.tab-pane {
min-height: 100px;
}
.card-head {
display: flex;
height: 40px;
align-items: center;
margin-bottom: 30px;
}
</style>

View File

@@ -0,0 +1,205 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>新增</span>
</a-button>
<a-button
danger
type="primary"
class="ele-btn-icon"
v-if="selection.length > 0"
@click="removeBatch"
>
<template #icon>
<DeleteOutlined />
</template>
<span>批量删除</span>
</a-button>
<!-- <a-button @click="batchMove" v-if="selection.length > 0">-->
<!-- <template #icon>-->
<!-- <UserSwitchOutlined />-->
<!-- </template>-->
<!-- 批量转移-->
<!-- </a-button>-->
<a-input-search
allow-clear
placeholder="请输入关键词"
v-model:value="searchText"
@pressEnter="search"
@search="search"
/>
</a-space>
</template>
<script lang="ts" setup>
import {
PlusOutlined,
DeleteOutlined,
UploadOutlined,
DownloadOutlined
} from '@ant-design/icons-vue';
import useSearch from '@/utils/use-search';
import type { CustomerParam } from '@/api/oa/customer/model';
import { ref, watch } from 'vue';
import { utils, read } from 'xlsx';
// import { assignObject } from 'ele-admin-pro';
import { message } from 'ant-design-vue/es';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: CustomerParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 表单数据
const { where } = useSearch<CustomerParam>({
name: '',
creditCode: '',
keywords: '',
userId: undefined
});
// 下来选项
// 搜索内容
const searchText = ref('');
/* 搜索 */
const search = () => {
where.keywords = searchText.value;
emit('search', where);
};
// 新增
const add = () => {
emit('add');
};
// 导入数据的列
const importTitle = ref<string[]>(['A', 'B', 'C', 'D', 'E', 'F', 'G']);
// 导入的数据
const importData = ref<Record<string, any>[]>([]);
// 导入数据二维数组形式
const importDataAoa = ref<(string | number)[][]>([]);
/* 导入本地 excel 文件 */
const importBatch = (file: File) => {
if (
![
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
].includes(file.type)
) {
message.error('只能选择 excel 文件');
return false;
}
if (file.size / 1024 / 1024 > 20) {
message.error('大小不能超过 20MB');
return false;
}
const reader = new FileReader();
reader.onload = (e) => {
const data = new Uint8Array(e.target?.result as any);
const workbook = read(data, { type: 'array' });
const sheetNames = workbook.SheetNames;
const worksheet = workbook.Sheets[sheetNames[0]];
// 解析成二维数组
const aoa = utils.sheet_to_json<string[]>(worksheet, { header: 1 });
// 生成表格需要的数据
let list: Record<string, any>[] = [];
let maxCols = 0;
let title: string[] = [];
aoa.forEach((d) => {
if (d.length > maxCols) {
maxCols = d.length;
}
const row = {};
for (let i = 0; i < d.length; i++) {
const key = getCharByIndex(i);
row[key] = d[i];
row['__colspan__' + key] = 1;
row['__rowspan__' + key] = 1;
}
list.push(row);
});
for (let i = 0; i < maxCols; i++) {
title.push(getCharByIndex(i));
}
importTitle.value = title;
importData.value = list;
importDataAoa.value = aoa;
};
console.log(importData.value);
console.log(importDataAoa.value);
reader.readAsArrayBuffer(file);
return false;
};
/* 生成Excel列字母序号 */
const getCharByIndex = (index: number) => {
const chars = [
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z'
];
if (index < chars.length) {
return chars[index];
}
const n = parseInt(String(index / chars.length));
const m = index % chars.length;
return chars[n] + chars[m];
};
// 转移
// const batchMove = () => {
// emit('batchMove');
// };
// 批量删除
const removeBatch = () => {
emit('remove');
};
const onClear = () => {
where.userId = undefined;
search();
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,306 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="historySettleId"
:columns="columns"
:datasource="datasource"
v-model:selection="selection"
tool-class="ele-toolbar-form"
:scroll="{ x: 800 }"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'status'">
<span>{{ rentRecordStatusList[record.status] }}</span>
</template>
<template v-if="column.key === 'createTime'">
<a-tooltip :title="`${toDateString(record.createTime)}`">
{{ timeAgo(record.createTime) }}
</a-tooltip>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<Edit v-model:visible="showEdit" :data="current" @done="reload" />
<!-- 合同详情弹窗 -->
<Info v-model:visible="showInfo" :data="current" @done="reload" />
<!-- 批量转移弹窗 -->
<!-- <CustomerMove v-model:visible="showMove" :data="selection" @done="batchMove" />-->
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import {
ExclamationCircleOutlined,
UserOutlined
} from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import { toDateString } from 'ele-admin-pro';
import Search from './components/search.vue';
import Edit from './components/edit.vue';
import Info from './components/info.vue';
import { timeAgo } from 'ele-admin-pro';
import { useUserStore } from '@/store/modules/user';
import { pageRentRecord, removeBatchRentRecord, removeRentRecord } from "@/api/tower/rentRecord";
import { RentRecord, RentRecordParam, rentRecordStatusList } from "@/api/tower/rentRecord/model";
const userStore = useUserStore();
// 当前用户信息
const loginUser = computed(() => userStore.info ?? {});
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<RentRecord[]>([]);
// 当前编辑数据
const current = ref<RentRecord | null>(null);
// 是否显示资产详情
const showInfo = ref(false);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageRentRecord({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
key: 'index',
width: 48,
align: 'center',
fixed: 'left',
hideInSetting: true,
customRender: ({ index }) => index + (tableRef.value?.tableIndex ?? 0)
},
{
title: '租用状态',
dataIndex: 'status',
key: 'status',
},
{
title: '起租日期',
dataIndex: 'startDate'
},
{
title: '停租日期',
dataIndex: 'stopDate'
},
{
title: '检测日期',
dataIndex: 'checkDate'
},
{
title: '报停日期',
dataIndex: 'reportStopDate'
},
{
title: '报停类型',
dataIndex: 'reportStopType'
},
{
title: '复工日期',
dataIndex: 'restoreDate'
},
{
title: '合同编号',
dataIndex: 'contractNo'
},
{
title: '设备名称',
dataIndex: 'equipmentName',
},
{
title: '设备型号',
dataIndex: 'equipmentModel'
},
{
title: '出厂编号',
dataIndex: 'factoryNo',
},
{
title: '备案编号',
dataIndex: 'filingNo'
},
{
title: '自编号',
dataIndex: 'equipmentNo'
},
{
title: '项目名称',
dataIndex: 'projectName',
fixed: 'right'
},
{
title: '报监编号',
dataIndex: 'reportMonitorNo',
fixed: 'right'
},
{
title: '承租单位',
dataIndex: 'companyName',
fixed: 'right'
},
{
title: '操作',
key: 'action',
width: 200,
align: 'center',
hideInSetting: true,
fixed: 'right'
}
]);
/* 搜索 */
const reload = (where?: RentRecordParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: RentRecord) => {
current.value = row ?? null;
showEdit.value = true;
};
const showSettle = ref<Boolean>(false)
const openSettle = (row?: RentRecord) => {
current.value = row ?? null;
showSettle.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 打开用户详情弹窗 */
const openInfo = (row?: RentRecord) => {
current.value = row ?? null;
showInfo.value = true;
};
/* 删除单个 */
const remove = (row: RentRecord) => {
const hide = message.loading('请求中..', 0);
removeRentRecord(row.rentRecordId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量转移 */
const batchMove = (userId) => {
console.log(userId, '批量转移0000');
console.log(selection.value);
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchRentRecord(
selection.value.map((d) => {
if (loginUser.value.userId === d.userId) {
return d.rentRecordId;
}
})
)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
</script>
<script lang="ts">
export default {
name: 'Accessory'
};
</script>
<style lang="less" scoped>
.sys-org-table :deep(.ant-table-body) {
overflow: auto !important;
overflow: overlay !important;
}
.sys-org-table :deep(.ant-table-pagination.ant-pagination) {
padding: 0 4px;
margin-bottom: 0;
}
</style>