新增:每月1日自动生成保养计划

This commit is contained in:
gxwebsoft
2024-02-23 13:57:48 +08:00
parent 25864fb449
commit e6caabbba8
12 changed files with 2017 additions and 386 deletions

View File

@@ -0,0 +1,108 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api';
import type {
SecurityRecord,
SecurityRecordParam
} from '@/api/tower/security-record/model';
/**
* 分页查询仓库
*/
export async function pageSecurityRecord(params: SecurityRecordParam) {
const res = await request.get<ApiResult<PageResult<SecurityRecord>>>(
'/tower/tower-security-record/page',
{
params
}
);
if (res.data.code === 0) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 查询仓库列表
*/
export async function listSecurityRecord(params?: SecurityRecordParam) {
const res = await request.get<ApiResult<SecurityRecord[]>>(
'/tower/tower-security-record',
{
params
}
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 添加仓库
*/
export async function addSecurityRecord(data: SecurityRecord) {
const res = await request.post<ApiResult<unknown>>(
'/tower/tower-security-record',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 修改仓库
*/
export async function updateSecurityRecord(data: SecurityRecord) {
const res = await request.put<ApiResult<unknown>>(
'/tower/tower-security-record',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 绑定仓库
*/
export async function bindSecurityRecord(data: SecurityRecord) {
const res = await request.put<ApiResult<unknown>>(
'/tower/tower-security-record/bind',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 删除仓库
*/
export async function removeSecurityRecord(id?: number) {
const res = await request.delete<ApiResult<unknown>>(
'/tower/tower-security-record/' + id
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 批量删除仓库
*/
export async function removeBatchSecurityRecord(data: (number | undefined)[]) {
const res = await request.delete<ApiResult<unknown>>(
'/tower/tower-security-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,31 @@
import type { PageParam } from '@/api';
export interface SecurityRecord {
securityRecordId?: number;
projectName?: string;
projectId?: number;
securityCode?: string;
regionName?: string;
longitude?: string;
latitude?: string;
country?: string;
province?: string;
city?: string;
region?: string;
address?: string;
status?: number;
userId?: number;
comments?: string;
sortNumber?: number;
}
/**
* 搜索条件
*/
export interface SecurityRecordParam extends PageParam {
securityId?: number;
projectId?: number;
projectName?: string;
status?: number;
keywords?: string;
}

View File

@@ -0,0 +1,105 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api';
import type { Security, SecurityParam } from '@/api/tower/security/model';
/**
* 分页查询仓库
*/
export async function pageSecurity(params: SecurityParam) {
const res = await request.get<ApiResult<PageResult<Security>>>(
'/tower/tower-security/page',
{
params
}
);
if (res.data.code === 0) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 查询仓库列表
*/
export async function listSecurity(params?: SecurityParam) {
const res = await request.get<ApiResult<Security[]>>(
'/tower/tower-security',
{
params
}
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 添加仓库
*/
export async function addSecurity(data: Security) {
const res = await request.post<ApiResult<unknown>>(
'/tower/tower-security',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 修改仓库
*/
export async function updateSecurity(data: Security) {
const res = await request.put<ApiResult<unknown>>(
'/tower/tower-security',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 绑定仓库
*/
export async function bindSecurity(data: Security) {
const res = await request.put<ApiResult<unknown>>(
'/tower/tower-security/bind',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 删除仓库
*/
export async function removeSecurity(id?: number) {
const res = await request.delete<ApiResult<unknown>>(
'/tower/tower-security/' + id
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 批量删除仓库
*/
export async function removeBatchSecurity(data: (number | undefined)[]) {
const res = await request.delete<ApiResult<unknown>>(
'/tower/tower-security/batch',
{
data
}
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}

View File

@@ -0,0 +1,31 @@
import type { PageParam } from '@/api';
export interface Security {
securityId?: number;
projectName?: string;
projectId?: number;
securityCode?: string;
regionName?: string;
longitude?: string;
latitude?: string;
country?: string;
province?: string;
city?: string;
region?: string;
address?: string;
status?: number;
userId?: number;
comments?: string;
sortNumber?: number;
}
/**
* 搜索条件
*/
export interface SecurityParam extends PageParam {
securityId?: number;
projectId?: number;
projectName?: string;
status?: number;
keywords?: string;
}

View File

@@ -54,30 +54,26 @@
/>
</a-form-item>
<a-form-item label="其他说明备注">
<a-input
allow-clear
v-model:value="form.remark"
/>
<a-input allow-clear v-model:value="form.remark" />
</a-form-item>
</a-col>
<a-col :md="8" :sm="24" :xs="24">
<a-form-item label="出租单位" v-bind="validateInfos.companyId">
{{ companyName }}
<!-- <SelectCompany-->
<!-- :placeholder="`请选择出租单位`"-->
<!-- v-model:value="companyName"-->
<!-- @done="chooseCompany"-->
<!-- />-->
<!-- <SelectCompany-->
<!-- :placeholder="`请选择出租单位`"-->
<!-- v-model:value="companyName"-->
<!-- @done="chooseCompany"-->
<!-- />-->
</a-form-item>
<a-form-item label="承租单位" v-bind="validateInfos.customerId">
{{ customerName }}
<!-- <SelectCustomer-->
<!-- :placeholder="`请选择承租单位`"-->
<!-- v-model:value="customerName"-->
<!-- @done="chooseCustomer"-->
<!-- />-->
<!-- <SelectCustomer-->
<!-- :placeholder="`请选择承租单位`"-->
<!-- v-model:value="customerName"-->
<!-- @done="chooseCustomer"-->
<!-- />-->
</a-form-item>
<a-form-item label="截止日期">
<a-date-picker
@@ -88,8 +84,14 @@
/>
</a-form-item>
<a-form-item label="合同存档">
<a-switch :checked="form.isInStock" :checked-value="1" :un-checked-value="0" checked-children="已归档"
un-checked-children="未归档" @change="changeInStock" />
<a-switch
:checked="form.isInStock"
:checked-value="1"
:un-checked-value="0"
checked-children="已归档"
un-checked-children="未归档"
@change="changeInStock"
/>
</a-form-item>
</a-col>
<a-col :md="8" :sm="24" :xs="24">
@@ -120,10 +122,16 @@
</template>
</a-input>
</a-form-item>
<a-form-item label="是否自动结算">
<a-switch :checked="form.autoSettle" :checked-value="1" :un-checked-value="0" checked-children=""
un-checked-children="" @change="changeAutoSettle" />
</a-form-item>
<a-form-item label="是否自动结算">
<a-switch
:checked="form.autoSettle"
:checked-value="1"
:un-checked-value="0"
checked-children=""
un-checked-children=""
@change="changeAutoSettle"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
@@ -139,13 +147,21 @@
</a-col>
<a-col :span="8">
<a-form-item label="欠收金额汇总">
<p style="margin-bottom: 0">{{ form.totalSettleAmount - form.totalIncomeAmount }}</p>
<p style="margin-bottom: 0">{{
form.totalSettleAmount - form.totalIncomeAmount
}}</p>
</a-form-item>
</a-col>
</a-row>
</a-form>
<div class="flex flex-col justify-start justify-start"
style="padding: 1rem 0;border-top: 1px solid #dcdfe6; border-bottom: 1px solid #dcdfe6">
<div
class="flex flex-col justify-start justify-start"
style="
padding: 1rem 0;
border-top: 1px solid #dcdfe6;
border-bottom: 1px solid #dcdfe6;
"
>
<div>
<p>合同电子存档</p>
<div class="flex justify-start">
@@ -164,8 +180,10 @@
</div>
</div>
</div>
<div class="flex justify-start items-center"
style="padding: 1rem 0;border-top: 1px solid #dcdfe6;">
<div
class="flex justify-start items-center"
style="padding: 1rem 0; border-top: 1px solid #dcdfe6"
>
<div class="flex justify-start items-center">
<unordered-list-outlined />
<span>合同签订设备清单</span>
@@ -211,62 +229,119 @@
</a-col>
</a-row>
<div style="min-height: 10rem">
<div class=" p-05 flex justify-center items-center" v-if="contractEquipmentList.length === 0">
<div
class="p-05 flex justify-center items-center"
v-if="contractEquipmentList.length === 0"
>
<span class="text-grey">暂无数据</span>
</div>
<template v-else>
<a-row class="text-center" v-for="(item, index) in contractEquipmentList"
:key="index">
<a-row
class="text-center"
v-for="(item, index) in contractEquipmentList"
:key="index"
>
<a-col :span="3" class="p-05 border-r-white">
<a-select placeholder="必选项" style="width: 100%" size="small" @change="changeEquipment($event, index)"
v-model:value="contractEquipmentList[index].equipmentId">
<a-select-option v-for="(item, index) in equipmentList" :key="index" :value="item.equipmentId">
<a-select
placeholder="必选项"
style="width: 100%"
size="small"
@change="changeEquipment($event, index)"
v-model:value="contractEquipmentList[index].equipmentId"
>
<a-select-option
v-for="(item, index) in equipmentList"
:key="index"
:value="item.equipmentId"
>
{{ item.name }}({{ item.model }})
</a-select-option>
</a-select>
</a-col>
<a-col :span="2" class="p-05 border-r-white">
<a-input v-model:value="contractEquipmentList[index].equipmentModel" size="small" readonly/>
<a-input
v-model:value="contractEquipmentList[index].equipmentModel"
size="small"
readonly
/>
</a-col>
<a-col :span="3" class="p-05 border-r-white">
<a-input v-model:value="contractEquipmentList[index].filingNo" size="small" readonly/>
<a-input
v-model:value="contractEquipmentList[index].filingNo"
size="small"
readonly
/>
</a-col>
<a-col :span="2" class="p-05 border-r-white">
<a-input v-model:value="contractEquipmentList[index].num" size="small" placeholder="必填项" type="number">
<a-input
v-model:value="contractEquipmentList[index].num"
size="small"
placeholder="必填项"
type="number"
>
<template #suffix></template>
</a-input>
</a-col>
<a-col :span="2" class="p-05 border-r-white">
<a-input v-model:value="contractEquipmentList[index].planRentMonth" size="small" placeholder="必填项"
type="number" />
<a-input
v-model:value="contractEquipmentList[index].planRentMonth"
size="small"
placeholder="必填项"
type="number"
/>
</a-col>
<a-col :span="2" class="p-05 border-r-white">
<a-input v-model:value="contractEquipmentList[index].rentAmount" size="small" placeholder="必填项"
type="number" />
<a-input
v-model:value="contractEquipmentList[index].rentAmount"
size="small"
placeholder="必填项"
type="number"
/>
</a-col>
<a-col :span="2" class="p-05 border-r-white">
<a-input v-model:value="contractEquipmentList[index].inOutAmount" size="small" placeholder="必填项"
type="number" />
<a-input
v-model:value="contractEquipmentList[index].inOutAmount"
size="small"
placeholder="必填项"
type="number"
/>
</a-col>
<a-col :span="2" class="p-05 border-r-white">
<a-input v-model:value="contractEquipmentList[index].workerAmount" size="small" placeholder="必填项"
type="number" />
<a-input
v-model:value="contractEquipmentList[index].workerAmount"
size="small"
placeholder="必填项"
type="number"
/>
</a-col>
<a-col :span="2" class="p-05 border-r-white">
<a-input v-model:value="contractEquipmentList[index].otherAmount" size="small" placeholder="必填项"
type="number" />
<a-input
v-model:value="contractEquipmentList[index].otherAmount"
size="small"
placeholder="必填项"
type="number"
/>
</a-col>
<a-col :span="2" class="p-05 border-r-white">
<a-input v-model:value="contractEquipmentList[index].preBuryAmount" size="small"
type="number" />
<a-input
v-model:value="contractEquipmentList[index].preBuryAmount"
size="small"
type="number"
/>
</a-col>
<a-col :span="1" class="p-05 border-r-white">
<a-input v-model:value="contractEquipmentList[index].remark" size="small" placeholder="可选项" />
<a-input
v-model:value="contractEquipmentList[index].remark"
size="small"
placeholder="可选项"
/>
</a-col>
<a-col :span="1" class="p-05">
<div class="flex justify-center items-center">
<close-circle-outlined style="font-size: 1.5rem; color: pink;cursor: pointer"
@click.native="delEquipment(index)" />
<close-circle-outlined
style="font-size: 1.5rem; color: pink; cursor: pointer"
@click.native="delEquipment(index)"
/>
</div>
</a-col>
</a-row>
@@ -285,362 +360,377 @@
</template>
<script lang="ts" setup>
import { ref, reactive, watch, computed } from "vue";
import { Form, message, UploadFile } from "ant-design-vue";
import { assignObject } from "ele-admin-pro";
import { updateContract, addContract } from "@/api/tower/contract";
import { useUserStore } from "@/store/modules/user";
import { Contract } from "@/api/tower/contract/model";
import dayjs from "dayjs";
import { ContractFile } from "@/api/tower/contractFile/model";
import { ref, reactive, watch, computed } from 'vue';
import { Form, message, UploadFile } from 'ant-design-vue';
import { assignObject } from 'ele-admin-pro';
import { updateContract, addContract } from '@/api/tower/contract';
import { useUserStore } from '@/store/modules/user';
import { Contract } from '@/api/tower/contract/model';
import dayjs from 'dayjs';
import { ContractFile } from '@/api/tower/contractFile/model';
import {
PlusOutlined,
UnorderedListOutlined,
CloseCircleOutlined, UploadOutlined
} from "@ant-design/icons-vue";
import { ContractEquipment } from "@/api/tower/contractEquipment/model";
import { TowerModel } from "@/api/tower/model/model";
import { listTowerModel } from "@/api/tower/model";
import { addBatchContractFile, listContractFile } from "@/api/tower/contractFile";
import { addBatchContractEquipment, listContractEquipment } from "@/api/tower/contractEquipment";
import { Company } from "@/api/system/company/model";
import { Customer } from "@/api/oa/customer/model";
import { listTowerEquipment } from "@/api/tower/equipment";
import { TowerEquipment } from "@/api/tower/equipment/model";
import { TOKEN_STORE_NAME } from "@/config/setting";
import { FileItem } from "ele-admin-pro/es/ele-file-list/types";
import { Equipment } from "@/api/apps/equipment/model";
import {
PlusOutlined,
UnorderedListOutlined,
CloseCircleOutlined,
UploadOutlined
} from '@ant-design/icons-vue';
import { ContractEquipment } from '@/api/tower/contractEquipment/model';
import { TowerModel } from '@/api/tower/model/model';
import { listTowerModel } from '@/api/tower/model';
import {
addBatchContractFile,
listContractFile
} from '@/api/tower/contractFile';
import {
addBatchContractEquipment,
listContractEquipment
} from '@/api/tower/contractEquipment';
import { Company } from '@/api/system/company/model';
import { Customer } from '@/api/oa/customer/model';
import { listTowerEquipment } from '@/api/tower/equipment';
import { TowerEquipment } from '@/api/tower/equipment/model';
import { TOKEN_STORE_NAME } from '@/config/setting';
import { FileItem } from 'ele-admin-pro/es/ele-file-list/types';
import { Equipment } from '@/api/apps/equipment/model';
const userStore = useUserStore();
// 当前用户信息
const loginUser = computed(() => userStore.info ?? {});
const token = localStorage.getItem(TOKEN_STORE_NAME);
const userStore = useUserStore();
// 当前用户信息
const loginUser = computed(() => userStore.info ?? {});
const token = localStorage.getItem(TOKEN_STORE_NAME);
const now = dayjs().format("YYYY-MM-DD HH:mm:ss");
const now = dayjs().format('YYYY-MM-DD HH:mm:ss');
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: Contract | null;
}>();
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: Contract | null;
}>();
const emit = defineEmits<{
(e: "done"): void;
(e: "update:visible", visible: boolean): void;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 用户信息
const form = reactive<Contract>({
contractId: undefined,
projectId: undefined,
companyId: undefined,
customerId: undefined,
customerContact: "",
contactNumber: "",
signDate: "",
startDate: "",
endDate: "",
contractAmount: undefined,
contactAgreeAmount: undefined,
totalSettleAmount: 0,
totalIncomeAmount: 0,
isInStock: 0,
autoSettle: 0,
payRate: undefined,
remark: "",
fileList: "[]"
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit("update:visible", value);
};
// 表单验证规则
const rules = reactive({
projectId: [
{
required: true,
message: "请选择项目",
trigger: "blur"
}
],
// contactNumber: [
// {
// required: true,
// message: "请输入合同编号",
// trigger: "blur"
// }
// ],
companyId: [
{
required: true,
message: "请选择出租单位",
trigger: "blur"
}
],
customerId: [
{
required: true,
message: "请选择承租单位",
trigger: "blur"
}
]
});
const projectName = ref<string>("");
const chooseProject = (res) => {
// console.log(res)
projectName.value = res.projectName;
form.projectId = res.projectId;
companyName.value = res.companyName;
form.companyId = res.companyId;
customerName.value = res.customerName;
form.customerId = res.customerId;
};
const companyName = ref<string>();
const chooseCompany = (res: Company) => {
companyName.value = res.companyName;
form.companyId = res.companyId;
};
const customerName = ref<string>("");
const chooseCustomer = (res: Customer) => {
customerName.value = res.name;
form.customerId = res.customerId;
form.customerContact = res.contact;
};
const changeInStock = (checked) => {
form.isInStock = checked;
};
const changeAutoSettle = (checked) => {
form.autoSettle = checked;
};
const fileList = ref<FileItem[]>([]);
interface FileInfo {
file: FileItem;
fileList: FileItem[];
}
const onFileUpload = (info: FileInfo) => {
let resFileList = [...info.fileList];
fileList.value = resFileList.map((file) => {
if (file.response) {
// Component will show file.url as link
file.url = file.response.url;
}
return file;
// 用户信息
const form = reactive<Contract>({
contractId: undefined,
projectId: undefined,
companyId: undefined,
customerId: undefined,
customerContact: '',
contactNumber: '',
signDate: '',
startDate: '',
endDate: '',
contractAmount: undefined,
contactAgreeAmount: undefined,
totalSettleAmount: 0,
totalIncomeAmount: 0,
isInStock: 0,
autoSettle: 0,
payRate: undefined,
remark: '',
fileList: '[]'
});
form.fileList = JSON.stringify(fileList.value);
};
const contractFileList = ref<ContractFile[]>([]);
const uploadFile = path => {
contractFileList.value.push({ path, contractId: 0, userId: loginUser.value.userId });
};
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
const delFile = index => {
contractFileList.value.splice(index, 1);
};
const getContractFileList = async () => {
contractFileList.value = await listContractFile({ contractId: props.data?.contractId });
};
const equipmentList = ref<TowerEquipment[]>([]);
const getEquipmentList = async () => {
equipmentList.value = await listTowerEquipment();
};
const equipmentModelList = ref<TowerModel[]>([]);
const getEquipmentModelList = async () => {
equipmentModelList.value = await listTowerModel();
};
const contractEquipmentList = ref<ContractEquipment[]>([]);
const getContractEquipmentList = async () => {
contractEquipmentList.value = await listContractEquipment({ contractId: props.data?.contractId });
};
const chooseEquipmentName = (res, index) => {
contractEquipmentList.value[index].equipmentName = res.name;
};
const delEquipment = index => {
contractEquipmentList.value.splice(index, 1);
};
const addEquipment = () => {
contractEquipmentList.value.push({
contractEquipmentId: null,
contractId: null,
equipmentId: undefined,
equipmentName: "",
equipmentModel: "",
num: null,
planRentMonth: null,
rentAmount: null,
inOutAmount: null,
workerAmount: null,
otherAmount: null,
preBuryAmount: null,
remark: null
});
};
const { resetFields, validate, validateInfos } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
validate()
.then(async () => {
loading.value = true;
// 去除空格
form.contactNumber = form.contactNumber?.replace(/\s*/g, "");
// 判断权限
// if (loginUser.value.roles?.[0].roleCode != 'admin'){
// form.status = '1';
// }
const data = {
...form
};
for (let i = 0; i < contractEquipmentList.value.length; i++) {
if (!contractEquipmentList.value[i].equipmentId) {
loading.value = false;
message.error("请选择设备");
return;
}
if (!contractEquipmentList.value[i].equipmentModel) {
loading.value = false;
message.error("请选择设备型号");
return;
}
if (!contractEquipmentList.value[i].num) {
loading.value = false;
message.error("请输入签订数量");
return;
}
if (contractEquipmentList.value[i].planRentMonth === null) {
loading.value = false;
message.error("请输入计划租期");
return;
}
if (contractEquipmentList.value[i].rentAmount === null) {
loading.value = false;
message.error("请输入租金");
return;
}
if (contractEquipmentList.value[i].inOutAmount === null) {
loading.value = false;
message.error("请输入进退场费");
return;
}
if (contractEquipmentList.value[i].workerAmount === null) {
loading.value = false;
message.error("请输入劳务费用");
return;
}
if (contractEquipmentList.value[i].otherAmount === null) {
loading.value = false;
message.error("请输入其他费用");
return;
}
// 表单验证规则
const rules = reactive({
projectId: [
{
required: true,
message: '请选择项目',
trigger: 'blur'
}
// 转字符串
const saveOrUpdate = isUpdate.value ? updateContract : addContract;
const res = await saveOrUpdate(data).catch((e) => {
loading.value = false;
message.error(e.message);
});
const contractId = res?.data;
// if (contractFileList.value.length > 0) await saveFileList(contractId);
if (contractEquipmentList.value.length > 0) await saveEquipmentList(contractId);
loading.value = false;
message.success(res?.message);
updateVisible(false);
emit("done");
],
// contactNumber: [
// {
// required: true,
// message: "请输入合同编号",
// trigger: "blur"
// }
// ],
companyId: [
{
required: true,
message: '请选择出租单位',
trigger: 'blur'
}
],
customerId: [
{
required: true,
message: '请选择承租单位',
trigger: 'blur'
}
]
});
})
.catch(() => {
const projectName = ref<string>('');
const chooseProject = (res) => {
// console.log(res)
projectName.value = res.projectName;
form.projectId = res.projectId;
companyName.value = res.companyName;
form.companyId = res.companyId;
customerName.value = res.customerName;
form.customerId = res.customerId;
};
const companyName = ref<string>();
const chooseCompany = (res: Company) => {
companyName.value = res.companyName;
form.companyId = res.companyId;
};
const customerName = ref<string>('');
const chooseCustomer = (res: Customer) => {
customerName.value = res.name;
form.customerId = res.customerId;
form.customerContact = res.contact;
};
const changeInStock = (checked) => {
form.isInStock = checked;
};
const changeAutoSettle = (checked) => {
form.autoSettle = checked;
};
const fileList = ref<FileItem[]>([]);
interface FileInfo {
file: FileItem;
fileList: FileItem[];
}
const onFileUpload = (info: FileInfo) => {
let resFileList = [...info.fileList];
fileList.value = resFileList.map((file) => {
if (file.response) {
// Component will show file.url as link
file.url = file.response.url;
}
return file;
});
};
form.fileList = JSON.stringify(fileList.value);
};
const saveFileList = async (contractId) => {
contractFileList.value.forEach((item, index) => {
contractFileList.value[index].contractId = contractId;
});
await addBatchContractFile(contractFileList.value);
};
const contractFileList = ref<ContractFile[]>([]);
const uploadFile = (path) => {
contractFileList.value.push({
path,
contractId: 0,
userId: loginUser.value.userId
});
};
const saveEquipmentList = async (contractId) => {
contractEquipmentList.value.forEach((item, index) => {
contractEquipmentList.value[index].contractId = contractId;
});
await addBatchContractEquipment(contractEquipmentList.value);
};
const delFile = (index) => {
contractFileList.value.splice(index, 1);
};
const changeEquipment = (equipmentId, index) => {
const equip = equipmentList.value.filter(item => item.equipmentId === equipmentId)[0]
console.log(equip)
contractEquipmentList.value[index].equipmentModel = equip.model
contractEquipmentList.value[index].filingNo = equip.filingNo
}
const getContractFileList = async () => {
contractFileList.value = await listContractFile({
contractId: props.data?.contractId
});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
getEquipmentList();
getEquipmentModelList();
if (props.data) {
const equipmentList = ref<TowerEquipment[]>([]);
const getEquipmentList = async () => {
equipmentList.value = await listTowerEquipment();
};
const equipmentModelList = ref<TowerModel[]>([]);
const getEquipmentModelList = async () => {
equipmentModelList.value = await listTowerModel();
};
const contractEquipmentList = ref<ContractEquipment[]>([]);
const getContractEquipmentList = async () => {
contractEquipmentList.value = await listContractEquipment({
contractId: props.data?.contractId
});
};
const chooseEquipmentName = (res, index) => {
contractEquipmentList.value[index].equipmentName = res.name;
};
const delEquipment = (index) => {
contractEquipmentList.value.splice(index, 1);
};
const addEquipment = () => {
contractEquipmentList.value.push({
contractEquipmentId: null,
contractId: null,
equipmentId: undefined,
equipmentName: '',
equipmentModel: '',
num: null,
planRentMonth: null,
rentAmount: null,
inOutAmount: null,
workerAmount: null,
otherAmount: null,
preBuryAmount: null,
remark: null
});
};
const { resetFields, validate, validateInfos } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
validate()
.then(async () => {
loading.value = true;
// 去除空格
form.contactNumber = form.contactNumber?.replace(/\s*/g, '');
// 判断权限
// if (loginUser.value.roles?.[0].roleCode != 'admin'){
// form.status = '1';
// }
const data = {
...form
};
for (let i = 0; i < contractEquipmentList.value.length; i++) {
if (!contractEquipmentList.value[i].equipmentId) {
loading.value = false;
message.error('请选择设备');
return;
}
if (!contractEquipmentList.value[i].equipmentModel) {
loading.value = false;
message.error('请选择设备型号');
return;
}
if (!contractEquipmentList.value[i].num) {
loading.value = false;
message.error('请输入签订数量');
return;
}
if (contractEquipmentList.value[i].planRentMonth === null) {
loading.value = false;
message.error('请输入计划租期');
return;
}
if (contractEquipmentList.value[i].rentAmount === null) {
loading.value = false;
message.error('请输入租金');
return;
}
if (contractEquipmentList.value[i].inOutAmount === null) {
loading.value = false;
message.error('请输入进退场费');
return;
}
if (contractEquipmentList.value[i].workerAmount === null) {
loading.value = false;
message.error('请输入劳务费用');
return;
}
if (contractEquipmentList.value[i].otherAmount === null) {
loading.value = false;
message.error('请输入其他费用');
return;
}
}
// 转字符串
const saveOrUpdate = isUpdate.value ? updateContract : addContract;
const res = await saveOrUpdate(data).catch((e) => {
loading.value = false;
message.error(e.message);
});
const contractId = res?.data;
// if (contractFileList.value.length > 0) await saveFileList(contractId);
if (contractEquipmentList.value.length > 0)
await saveEquipmentList(contractId);
loading.value = false;
assignObject(form, props.data);
isUpdate.value = true;
getContractFileList();
getContractEquipmentList();
projectName.value = props.data.projectName;
customerName.value = props.data.customerName;
companyName.value = props.data.companyName;
if (props.data.fileList) {
fileList.value = JSON.parse(props.data.fileList);
message.success(res?.message);
updateVisible(false);
emit('done');
})
.catch(() => {});
};
const saveFileList = async (contractId) => {
contractFileList.value.forEach((item, index) => {
contractFileList.value[index].contractId = contractId;
});
await addBatchContractFile(contractFileList.value);
};
const saveEquipmentList = async (contractId) => {
contractEquipmentList.value.forEach((item, index) => {
contractEquipmentList.value[index].contractId = contractId;
});
await addBatchContractEquipment(contractEquipmentList.value);
};
const changeEquipment = (equipmentId, index) => {
const equip = equipmentList.value.filter(
(item) => item.equipmentId === equipmentId
)[0];
console.log(equip);
contractEquipmentList.value[index].equipmentModel = equip.model;
contractEquipmentList.value[index].filingNo = equip.filingNo;
};
watch(
() => props.visible,
(visible) => {
if (visible) {
getEquipmentList();
getEquipmentModelList();
if (props.data) {
loading.value = false;
assignObject(form, props.data);
isUpdate.value = true;
getContractFileList();
getContractEquipmentList();
projectName.value = props.data.projectName;
customerName.value = props.data.customerName;
companyName.value = props.data.companyName;
if (props.data.fileList) {
fileList.value = JSON.parse(props.data.fileList);
}
} else {
isUpdate.value = false;
}
} else {
isUpdate.value = false;
projectName.value = customerName.value = companyName.value = '';
contractFileList.value = [];
fileList.value = [];
resetFields();
}
} else {
projectName.value = customerName.value = companyName.value = "";
contractFileList.value = [];
fileList.value = [];
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;
}
.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,192 @@
<!-- 编辑弹窗 -->
<template xmlns="">
<ele-modal
:width="500"
:visible="visible"
:confirm-loading="loading"
:title="isUpdate ? '修改保养计划' : '新建保养计划'"
:body-style="{ paddingBottom: '8px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 6, sm: 6, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 18, sm: 20, xs: 24 } : { flex: '1' }
"
>
<a-row :gutter="16">
<a-col
v-bind="styleResponsive ? { md: 22, sm: 24, xs: 24 } : { span: 12 }"
>
<a-form-item label="项目名称" name="name">
<ProjectSelectModel
:placeholder="`请选择项目`"
v-model:value="form.projectName"
@done="chooseProject"
/>
</a-form-item>
<a-form-item label="所在地" name="address">
<a-input
allow-clear
placeholder="请输入项目地址"
v-model:value="form.address"
/>
</a-form-item>
</a-col>
</a-row>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { message } from 'ant-design-vue/es';
import type { FormInstance, Rule } from 'ant-design-vue/es/form';
import { storeToRefs } from 'pinia';
import { useThemeStore } from '@/store/modules/theme';
import useFormData from '@/utils/use-form-data';
import type { Security } from '@/api/tower/security/model';
import { addSecurity, updateSecurity } from '@/api/tower/security';
import { Project } from '@/api/tower/project/model';
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: Security | null;
}>();
const formRef = ref<FormInstance | null>(null);
// 是否是修改
const isUpdate = ref(false);
// 提交状态
const loading = ref(false);
// 表单数据
const { form, resetFields, assignFields } = useFormData<Security>({
securityId: undefined,
projectId: undefined,
projectName: undefined,
securityCode: undefined,
address: '',
status: 0,
sortNumber: 100
});
// 表单验证规则
const rules = reactive<Record<string, Rule[]>>({
name: [
{
required: true,
message: '请输入设备名称',
type: 'string',
trigger: 'blur'
}
],
model: [
{
required: true,
message: '请输入设备型号',
type: 'string',
trigger: 'blur'
}
],
yearLife: [
{
required: true,
message: '请输入设备使用年限',
type: 'number',
trigger: 'blur'
}
],
sortNumber: [
{
required: true,
message: '请输入排序号',
type: 'number',
trigger: 'blur'
}
]
});
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const categoryForm = {
...form
};
const saveOrUpdate = isUpdate.value ? updateSecurity : addSecurity;
saveOrUpdate(categoryForm)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
const chooseProject = (row: Project) => {
console.log(row);
form.projectId = row.projectId;
form.projectName = row.projectName;
form.address = row.projectAddress;
};
watch(
() => props.visible,
(visible) => {
if (visible) {
if (props.data) {
assignFields({
...props.data
});
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
formRef.value?.clearValidate();
}
}
);
</script>
<style lang="less">
.tab-pane {
min-height: 300px;
}
.ml-10 {
margin-left: 5px;
}
</style>

View File

@@ -0,0 +1,222 @@
<template>
<div class="ele-body">
<a-card :bordered="false">
<!-- 表格 -->
<ele-pro-table
ref="tableRef"
row-key="securityRecordId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
:expand-icon-column-index="1"
:scroll="{ x: 1200 }"
cache-key="towerModel"
>
<template #toolbar>
<a-space>
<ProjectSelectModel
:placeholder="`请选择项目`"
v-model:value="projectName"
@done="chooseProject"
/>
<a-radio-group v-model:value="searchStatus" @change="onStatus">
<a-radio-button value="0">正常</a-radio-button>
<a-radio-button value="1">待修</a-radio-button>
<a-radio-button value="2">异常已修</a-radio-button>
<a-radio-button value="3">异常未修</a-radio-button>
</a-radio-group>
<a-range-picker
v-model:value="dateRange"
@change="onRange"
value-format="YYYY-MM-DD"
class="ele-fluid"
/>
</a-space>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'userId'">
{{ `${record.nickname}` }}
</template>
<template v-if="column.key === 'images'">
<a-image :src="record.images" :width="120" />
</template>
<template v-if="column.key === 'longitude'">
{{ `${record.longitude}, ${record.latitude}` }}
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status == 0">正常</a-tag>
<a-tag v-if="record.status == 1" color="red">待修</a-tag>
<a-tag v-if="record.status == 2" color="green">异常已修</a-tag>
<a-tag v-if="record.status == 3" color="orange">异常未修</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
placement="topRight"
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<SecurityRecordEdit
v-model:visible="showEdit"
:data="current"
@done="reload"
/>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { message } from 'ant-design-vue/es';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import { messageLoading, toDateString } from 'ele-admin-pro/es';
import type { EleProTable } from 'ele-admin-pro/es';
import type {
SecurityRecord,
SecurityRecordParam
} from '@/api/tower/security-record/model';
import {
pageSecurityRecord,
removeSecurityRecord
} from '@/api/tower/security-record';
import SecurityRecordEdit from './components/security-record-edit.vue';
import { Project } from '@/api/tower/project/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '操作人',
dataIndex: 'userId',
key: 'userId'
},
{
title: '现场图片',
dataIndex: 'images',
key: 'images'
},
{
title: '定位',
dataIndex: 'longitude',
key: 'longitude'
},
{
title: '备注说明',
dataIndex: 'comments',
key: 'comments'
},
{
title: '完成状态',
dataIndex: 'status',
key: 'status',
width: 180
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => toDateString(text)
}
]);
// 当前编辑数据
const current = ref<SecurityRecord | null>(null);
const searchText = ref('');
const searchStatus = ref('');
// 是否显示编辑弹窗
const showEdit = ref(false);
const projectName = ref('');
// 日期范围选择
const dateRange = ref<[string, string]>(['', '']);
/* 搜索 */
const onRange = () => {
const [d1, d2] = dateRange.value ?? [];
reload({
createTimeStart: d1 ? d1 + ' 00:00:00' : '',
createTimeEnd: d2 ? d2 + ' 23:59:59' : ''
});
};
// 表格数据源
const datasource: DatasourceFunction = ({ where }) => {
if (searchText.value && searchText.value != '') {
where.keywords = searchText.value;
}
return pageSecurityRecord({ ...where });
};
/* 刷新表格 */
const reload = (where?: SecurityRecordParam) => {
tableRef?.value?.reload({ where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: SecurityRecord | null, id?: number) => {
current.value = row ?? null;
showEdit.value = true;
};
const search = (searchText) => {
reload({ keywords: searchText });
};
const chooseProject = (row: Project) => {
reload({ projectId: row.projectId });
};
const onStatus = (e: any) => {
console.log(e.target.value);
const status = e.target.value;
reload({ status });
};
/* 删除单个 */
const remove = (row: SecurityRecord) => {
const hide = messageLoading('请求中..', 0);
removeSecurityRecord(row.securityRecordId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 自定义行属性 */
const customRow = (record: SecurityRecord) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
</script>
<script lang="ts">
export default {
name: 'TowerSecurityRecord'
};
</script>

View File

@@ -0,0 +1,249 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="740"
:visible="visible"
:confirm-loading="loading"
:title="isUpdate ? '修改分类' : '新建分类'"
:body-style="{ paddingBottom: '8px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 6, sm: 4, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 18, sm: 20, xs: 24 } : { flex: '1' }
"
>
<a-row :gutter="16">
<a-col
v-bind="styleResponsive ? { md: 12, sm: 24, xs: 24 } : { span: 12 }"
>
<a-form-item label="上级分类" name="parentId">
<a-tree-select
allow-clear
:tree-data="categoryList"
tree-default-expand-all
placeholder="请选择上级分类"
:value="form.parentId || undefined"
:dropdown-style="{ maxHeight: '360px', overflow: 'auto' }"
@update:value="(value?: number) => (form.parentId = value)"
/>
</a-form-item>
<a-form-item label="设备型号" name="title">
<a-input
allow-clear
placeholder="请输入分类名称"
v-model:value="form.title"
@pressEnter="save"
/>
</a-form-item>
<a-form-item label="使用年限" name="yearLife">
<a-input
allow-clear
placeholder="请输入分类名称"
v-model:value="form.yearLife"
@pressEnter="save"
/>
</a-form-item>
</a-col>
</a-row>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { message } from 'ant-design-vue/es';
import type { FormInstance, Rule } from 'ant-design-vue/es/form';
import { storeToRefs } from 'pinia';
import { useThemeStore } from '@/store/modules/theme';
import useFormData from '@/utils/use-form-data';
import type { Security } from '@/api/tower/security/model';
import { addSecurity, updateSecurity } from '@/api/tower/security';
import { FILE_SERVER } from "@/config/setting";
import { ItemType } from "ele-admin-pro/es/ele-image-upload/types";
import { uploadFile } from "@/api/system/file";
import { LoginRecordParam } from "@/api/system/login-record/model";
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: Security | null;
// 上级分类id
parentId?: number;
// 全部分类数据
categoryList: Security[];
}>();
// 表单数据
const { form, resetFields, assignFields } = useFormData<Security>({
securityId: undefined,
projectName: undefined,
securityCode: undefined,
regionName: '',
status: 0,
address: undefined,
sortNumber: 100
});
//
const formRef = ref<FormInstance | null>(null);
// 是否是修改
const isUpdate = ref(false);
// 提交状态
const loading = ref(false);
// 已上传数据, 可赋初始值用于回显
const images = ref([]);
// 表单验证规则
const rules = reactive<Record<string, Rule[]>>({
title: [
{
required: true,
message: '请输入分类名称',
type: 'string',
trigger: 'blur'
}
],
sortNumber: [
{
required: true,
message: '请输入排序号',
type: 'number',
trigger: 'blur'
}
],
meta: [
{
type: 'string',
validator: async (_rule: Rule, value: string) => {
if (value) {
const msg = '请输入正确的JSON格式';
try {
const obj = JSON.parse(value);
if (typeof obj !== 'object' || obj === null) {
return Promise.reject(msg);
}
} catch (_e) {
return Promise.reject(msg);
}
}
return Promise.resolve();
},
trigger: 'blur'
}
]
});
// 上传文件
const onUpload = (d: ItemType) => {
uploadFile(<File>d.file)
.then((result) => {
form.image = result.path;
message.success('上传成功');
})
.catch((e) => {
message.error(e.message);
});
};
const onClose = () => {
form.image = ''
}
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const categoryForm = {
...form,
parentId: form.parentId || 0
};
const saveOrUpdate = isUpdate.value ? updateSecurity : addSecurity;
saveOrUpdate(categoryForm)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
const updateHideValue = (value: boolean) => {
form.status = value ? 0 : 1;
};
watch(
() => props.visible,
(visible) => {
if (visible) {
if (props.data) {
// 头像赋值
images.value = [];
if(props.data.image){
images.value.push({ uid:1, url: props.data.image, status: '' });
}
assignFields({
...props.data,
parentId:
props.data.parentId === 0 ? undefined : props.data.parentId
});
isUpdate.value = true;
} else {
form.parentId = props.parentId;
isUpdate.value = false;
}
} else {
resetFields();
formRef.value?.clearValidate();
}
}
);
</script>
<script lang="ts">
import * as icons from '@/layout/menu-icons';
export default {
components: icons,
data() {
return {
iconData: [
{
title: '已引入的图标',
icons: Object.keys(icons)
}
]
};
}
};
</script>

View File

@@ -0,0 +1,192 @@
<!-- 编辑弹窗 -->
<template xmlns="">
<ele-modal
:width="500"
:visible="visible"
:confirm-loading="loading"
:title="isUpdate ? '修改保养计划' : '新建保养计划'"
:body-style="{ paddingBottom: '8px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 6, sm: 6, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 18, sm: 20, xs: 24 } : { flex: '1' }
"
>
<a-row :gutter="16">
<a-col
v-bind="styleResponsive ? { md: 22, sm: 24, xs: 24 } : { span: 12 }"
>
<a-form-item label="项目名称" name="name">
<ProjectSelectModel
:placeholder="`请选择项目`"
v-model:value="form.projectName"
@done="chooseProject"
/>
</a-form-item>
<a-form-item label="所在地" name="address">
<a-input
allow-clear
placeholder="请输入项目地址"
v-model:value="form.address"
/>
</a-form-item>
</a-col>
</a-row>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { message } from 'ant-design-vue/es';
import type { FormInstance, Rule } from 'ant-design-vue/es/form';
import { storeToRefs } from 'pinia';
import { useThemeStore } from '@/store/modules/theme';
import useFormData from '@/utils/use-form-data';
import type { Security } from '@/api/tower/security/model';
import { addSecurity, updateSecurity } from '@/api/tower/security';
import { Project } from '@/api/tower/project/model';
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: Security | null;
}>();
const formRef = ref<FormInstance | null>(null);
// 是否是修改
const isUpdate = ref(false);
// 提交状态
const loading = ref(false);
// 表单数据
const { form, resetFields, assignFields } = useFormData<Security>({
securityId: undefined,
projectId: undefined,
projectName: undefined,
securityCode: undefined,
address: '',
status: 0,
sortNumber: 100
});
// 表单验证规则
const rules = reactive<Record<string, Rule[]>>({
name: [
{
required: true,
message: '请输入设备名称',
type: 'string',
trigger: 'blur'
}
],
model: [
{
required: true,
message: '请输入设备型号',
type: 'string',
trigger: 'blur'
}
],
yearLife: [
{
required: true,
message: '请输入设备使用年限',
type: 'number',
trigger: 'blur'
}
],
sortNumber: [
{
required: true,
message: '请输入排序号',
type: 'number',
trigger: 'blur'
}
]
});
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const categoryForm = {
...form
};
const saveOrUpdate = isUpdate.value ? updateSecurity : addSecurity;
saveOrUpdate(categoryForm)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
const chooseProject = (row: Project) => {
console.log(row);
form.projectId = row.projectId;
form.projectName = row.projectName;
form.address = row.projectAddress;
};
watch(
() => props.visible,
(visible) => {
if (visible) {
if (props.data) {
assignFields({
...props.data
});
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
formRef.value?.clearValidate();
}
}
);
</script>
<style lang="less">
.tab-pane {
min-height: 300px;
}
.ml-10 {
margin-left: 5px;
}
</style>

View File

@@ -0,0 +1,169 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="1000"
:visible="visible"
:confirm-loading="loading"
:title="isUpdate ? '保养记录' : '保养记录'"
:body-style="{ paddingBottom: '8px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 6, sm: 6, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 18, sm: 20, xs: 24 } : { flex: '1' }
"
>
<a-row :gutter="16">
<a-col
v-bind="styleResponsive ? { md: 22, sm: 24, xs: 24 } : { span: 12 }"
/>
</a-row>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { message } from 'ant-design-vue/es';
import type { FormInstance, Rule } from 'ant-design-vue/es/form';
import { storeToRefs } from 'pinia';
import { useThemeStore } from '@/store/modules/theme';
import useFormData from '@/utils/use-form-data';
import type { TowerModel } from '@/api/tower/model/model';
import { addTowerModel, updateTowerModel } from '@/api/tower/model';
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: TowerModel | null;
}>();
const formRef = ref<FormInstance | null>(null);
// 是否是修改
const isUpdate = ref(false);
// 提交状态
const loading = ref(false);
// 表单数据
const { form, resetFields, assignFields } = useFormData<TowerModel>({
name: '',
modelId: undefined,
model: undefined,
image: '',
status: 0,
yearLife: undefined,
sortNumber: 100
});
// 表单验证规则
const rules = reactive<Record<string, Rule[]>>({
name: [
{
required: true,
message: '请输入设备名称',
type: 'string',
trigger: 'blur'
}
],
model: [
{
required: true,
message: '请输入设备型号',
type: 'string',
trigger: 'blur'
}
],
yearLife: [
{
required: true,
message: '请输入设备使用年限',
type: 'number',
trigger: 'blur'
}
],
sortNumber: [
{
required: true,
message: '请输入排序号',
type: 'number',
trigger: 'blur'
}
]
});
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const categoryForm = {
...form
};
const saveOrUpdate = isUpdate.value ? updateTowerModel : addTowerModel;
saveOrUpdate(categoryForm)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
watch(
() => props.visible,
(visible) => {
if (visible) {
if (props.data) {
assignFields({
...props.data
});
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
formRef.value?.clearValidate();
}
}
);
</script>
<style lang="less">
.tab-pane {
min-height: 300px;
}
.ml-10 {
margin-left: 5px;
}
</style>

View File

@@ -0,0 +1,242 @@
<template>
<div class="ele-body">
<a-card :bordered="false">
<!-- 表格 -->
<ele-pro-table
ref="tableRef"
row-key="securityId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
:expand-icon-column-index="1"
:scroll="{ x: 1200 }"
cache-key="towerModel"
>
<template #toolbar>
<a-space>
<a-button type="primary" class="ele-btn-icon" @click="openEdit()">
<template #icon>
<plus-outlined />
</template>
<span>新建</span>
</a-button>
<!-- 搜索表单 -->
<a-input-search
allow-clear
v-model:value="searchText"
placeholder="请输入搜索关键词"
@search="search"
@pressEnter="search"
/>
<a-radio-group v-model:value="searchStatus" @change="onStatus">
<a-radio-button value="0">正常</a-radio-button>
<a-radio-button value="1">待修</a-radio-button>
<a-radio-button value="2">异常已修</a-radio-button>
<a-radio-button value="3">异常未修</a-radio-button>
</a-radio-group>
<a-range-picker
v-model:value="dateRange"
@change="onRange"
value-format="YYYY-MM-DD"
class="ele-fluid"
/>
</a-space>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'status'">
<a-tag v-if="record.status == 0">正常</a-tag>
<a-tag v-if="record.status == 1" color="red">待修</a-tag>
<a-tag v-if="record.status == 2" color="green">异常已修</a-tag>
<a-tag v-if="record.status == 3" color="orange">异常未修</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openRecord(record)">保养记录</a>
<a-divider type="vertical" />
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
placement="topRight"
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<SecurityEdit v-model:visible="showEdit" :data="current" @done="reload" />
<!-- 保养记录 -->
<SecurityRecord
v-model:visible="showRecord"
:data="current"
@done="reload"
/>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { message } from 'ant-design-vue/es';
import { PlusOutlined } from '@ant-design/icons-vue';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import { messageLoading, toDateString } from 'ele-admin-pro/es';
import type { EleProTable } from 'ele-admin-pro/es';
import type { Security, SecurityParam } from '@/api/tower/security/model';
import {
pageSecurity,
listSecurity,
removeSecurity
} from '@/api/tower/security';
import SecurityEdit from './components/security-edit.vue';
import SecurityRecord from './components/security-record.vue';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格列配置
const columns = ref<ColumnItem[]>([
// {
// key: 'index',
// width: 48,
// align: 'center',
// fixed: 'left',
// hideInSetting: true,
// customRender: ({ index }) => index + (tableRef.value?.tableIndex ?? 0)
// },
// {
// title: 'ID',
// dataIndex: 'securityId',
// key: 'securityId'
// },
{
title: '保养编号',
dataIndex: 'securityCode',
key: 'securityCode'
},
{
title: '项目名称',
dataIndex: 'projectName',
key: 'projectName'
},
{
title: '所在地',
dataIndex: 'address',
key: 'address',
showSorterTooltip: false,
ellipsis: true
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 180
},
{
title: '生成时间',
dataIndex: 'createTime',
width: 180,
customRender: ({ text }) => toDateString(text)
},
{
title: '操作',
key: 'action',
width: 200,
align: 'center'
}
]);
// 当前编辑数据
const current = ref<Security | null>(null);
const searchText = ref('');
const searchStatus = ref('');
// 是否显示编辑弹窗
const showEdit = ref(false);
const showRecord = ref(false);
// 日期范围选择
const dateRange = ref<[string, string]>(['', '']);
/* 搜索 */
const onRange = () => {
const [d1, d2] = dateRange.value ?? [];
reload({
createTimeStart: d1 ? d1 + ' 00:00:00' : '',
createTimeEnd: d2 ? d2 + ' 23:59:59' : ''
});
};
// 表格数据源
const datasource: DatasourceFunction = ({ where }) => {
if (searchText.value && searchText.value != '') {
where.keywords = searchText.value;
}
return pageSecurity({ ...where });
};
/* 刷新表格 */
const reload = (where?: SecurityParam) => {
tableRef?.value?.reload({ where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: Security | null, id?: number) => {
current.value = row ?? null;
showEdit.value = true;
};
const openRecord = (row?: Security, id?: number) => {
current.value = row ?? null;
showRecord.value = true;
};
const search = (searchText) => {
reload({ keywords: searchText });
};
const onStatus = (e: any) => {
console.log(e.target.value);
const status = e.target.value;
reload({ status });
};
/* 删除单个 */
const remove = (row: Security) => {
const hide = messageLoading('请求中..', 0);
removeSecurity(row.securityId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 自定义行属性 */
const customRow = (record: Security) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
</script>
<script lang="ts">
export default {
name: 'TowerSecurity'
};
</script>