完成:安全维保管理模块

This commit is contained in:
gxwebsoft
2024-02-25 15:23:15 +08:00
parent e6caabbba8
commit 726bb3c163
10 changed files with 1525 additions and 366 deletions

View File

@@ -1,3 +1,3 @@
VITE_APP_NAME=后台管理系统 VITE_APP_NAME=后台管理系统
VITE_API_URL=http://127.0.0.1:10041/api #VITE_API_URL=http://127.0.0.1:10041/api
#VITE_API_URL=http://1.14.159.185:10041/api VITE_API_URL=http://1.14.159.185:10041/api

View File

@@ -1,6 +1,7 @@
import type { PageParam } from '@/api'; import type { PageParam } from '@/api';
export interface Security { export interface Security {
type: number;
securityId?: number; securityId?: number;
projectName?: string; projectName?: string;
projectId?: number; projectId?: number;
@@ -13,6 +14,10 @@ export interface Security {
city?: string; city?: string;
region?: string; region?: string;
address?: string; address?: string;
problem?: string;
processingMethod?: string;
files?: string;
replaceAccessories?: number;
status?: number; status?: number;
userId?: number; userId?: number;
comments?: string; comments?: 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

@@ -194,17 +194,17 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, watch } from "vue"; import { ref, reactive, watch } from 'vue';
import { Form, message } from "ant-design-vue"; import { Form, message } from 'ant-design-vue';
import { assignObject } from "ele-admin-pro"; import { assignObject } from 'ele-admin-pro';
import { addAccessory, updateAccessory } from "@/api/tower/accessory"; import { addAccessory, updateAccessory } from '@/api/tower/accessory';
import { Accessory } from "@/api/tower/accessory/model"; import { Accessory } from '@/api/tower/accessory/model';
import { useThemeStore } from "@/store/modules/theme"; import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from "pinia"; import { storeToRefs } from 'pinia';
import { FormInstance } from "ant-design-vue/es/form"; import { FormInstance } from 'ant-design-vue/es/form';
import { Customer } from "@/api/oa/customer/model"; import { Customer } from '@/api/oa/customer/model';
import { Category } from "@/api/goods/category/model"; import { Category } from '@/api/goods/category/model';
import { TowerModel } from "@/api/tower/model/model"; import { TowerModel } from '@/api/tower/model/model';
// 是否是修改 // 是否是修改
const isUpdate = ref(false); const isUpdate = ref(false);
@@ -224,8 +224,8 @@ const props = defineProps<{
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: "done"): void; (e: 'done'): void;
(e: "update:visible", visible: boolean): void; (e: 'update:visible', visible: boolean): void;
}>(); }>();
// 提交状态 // 提交状态
@@ -238,35 +238,35 @@ const formRef = ref<FormInstance | null>(null);
// 用户信息 // 用户信息
const form = reactive<Accessory>({ const form = reactive<Accessory>({
accessoryId: undefined, accessoryId: undefined,
accessoryName: "", accessoryName: '',
accessoryNo: "", accessoryNo: '',
accessoryCategory: undefined, accessoryCategory: undefined,
accessoryModel: "", accessoryModel: '',
accessoryModelMultiple: [], accessoryModelMultiple: [],
accessorySpecs: "", accessorySpecs: '',
accessoryPrice: 0, accessoryPrice: 0,
accessoryUnit: "", accessoryUnit: '',
accessoryNum: 0, accessoryNum: 0,
accessoryStock: "", accessoryStock: '',
companyName: "", companyName: '',
manufactor: "", manufactor: '',
accessoryWeight: "", accessoryWeight: '',
manufactorNo: "", manufactorNo: '',
lifeYear: undefined, lifeYear: undefined,
factoryDate: "", factoryDate: '',
scrapDate: "", scrapDate: '',
status: undefined, status: undefined,
comments: "", comments: '',
sortNumber: undefined, sortNumber: undefined,
userId: undefined, userId: undefined,
createTime: "", createTime: '',
updateTime: "", updateTime: '',
buyDate: "" buyDate: ''
}); });
/* 更新visible */ /* 更新visible */
const updateVisible = (value: boolean) => { const updateVisible = (value: boolean) => {
emit("update:visible", value); emit('update:visible', value);
}; };
// 表单验证规则 // 表单验证规则
@@ -274,41 +274,41 @@ const rules = reactive({
accessoryCategory: [ accessoryCategory: [
{ {
required: true, required: true,
type: "array", type: 'array',
message: "请输入配件分类", message: '请输入配件分类',
trigger: "blur" trigger: 'blur'
} }
], ],
accessoryName: [ accessoryName: [
{ {
required: true, required: true,
type: "string", type: 'string',
message: "请输入生成厂家", message: '请输入生成厂家',
trigger: "blur" trigger: 'blur'
} }
], ],
accessoryModel: [ accessoryModel: [
{ {
required: true, required: true,
type: "string", type: 'string',
message: "请输入设备型号", message: '请输入设备型号',
trigger: "blur" trigger: 'blur'
} }
], ],
accessorySpecs: [ accessorySpecs: [
{ {
required: true, required: true,
type: "string", type: 'string',
message: "请输入设备规格", message: '请输入设备规格',
trigger: "blur" trigger: 'blur'
} }
], ],
accessoryUnit: [ accessoryUnit: [
{ {
required: true, required: true,
type: "string", type: 'string',
message: "请输入单位", message: '请输入单位',
trigger: "blur" trigger: 'blur'
} }
] ]
}); });
@@ -349,15 +349,14 @@ const save = () => {
loading.value = false; loading.value = false;
message.success(msg); message.success(msg);
updateVisible(false); updateVisible(false);
emit("done"); emit('done');
}) })
.catch((e) => { .catch((e) => {
loading.value = false; loading.value = false;
message.error(e.message); message.error(e.message);
}); });
}) })
.catch(() => { .catch(() => {});
});
}; };
watch( watch(
@@ -367,8 +366,8 @@ watch(
if (props.data) { if (props.data) {
assignObject(form, props.data); assignObject(form, props.data);
const accessoryCategory = String(props.data.accessoryCategory); const accessoryCategory = String(props.data.accessoryCategory);
const split = accessoryCategory?.split(" / "); const split = accessoryCategory?.split(' / ');
if (typeof split == "object") { if (typeof split == 'object') {
form.accessoryCategory = split; form.accessoryCategory = split;
} }
if (props.data.accessoryModelMultiple) { if (props.data.accessoryModelMultiple) {

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="ele-body"> <div class="ele-body">
<a-card :bordered="false"> <a-card :bordered="false" style="margin-top: 10px">
<!-- 表格 --> <!-- 表格 -->
<ele-pro-table <ele-pro-table
ref="tableRef" ref="tableRef"
@@ -14,20 +14,12 @@
> >
<template #toolbar> <template #toolbar>
<a-space> <a-space>
<a-button type="primary" class="ele-btn-icon" @click="openEdit()"> <!-- <a-button type="primary" class="ele-btn-icon" @click="openEdit()">-->
<template #icon> <!-- <template #icon>-->
<plus-outlined /> <!-- <plus-outlined />-->
</template> <!-- </template>-->
<span>新建</span> <!-- <span>新建</span>-->
</a-button> <!-- </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-group v-model:value="searchStatus" @change="onStatus">
<a-radio-button value="0">正常</a-radio-button> <a-radio-button value="0">正常</a-radio-button>
<a-radio-button value="1">待修</a-radio-button> <a-radio-button value="1">待修</a-radio-button>
@@ -40,6 +32,14 @@
value-format="YYYY-MM-DD" value-format="YYYY-MM-DD"
class="ele-fluid" class="ele-fluid"
/> />
<!-- 搜索表单 -->
<a-input-search
allow-clear
v-model:value="searchText"
placeholder="请输入搜索关键词"
@search="search"
@pressEnter="search"
/>
</a-space> </a-space>
</template> </template>
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
@@ -51,8 +51,8 @@
</template> </template>
<template v-if="column.key === 'action'"> <template v-if="column.key === 'action'">
<a-space> <a-space>
<a @click="openRecord(record)">保养记录</a> <!-- <a @click="openRecord(record)">保养记录</a>-->
<a-divider type="vertical" /> <!-- <a-divider type="vertical" />-->
<a @click="openEdit(record)">修改</a> <a @click="openEdit(record)">修改</a>
<a-divider type="vertical" /> <a-divider type="vertical" />
<a-popconfirm <a-popconfirm
@@ -89,11 +89,7 @@
import { messageLoading, toDateString } from 'ele-admin-pro/es'; import { messageLoading, toDateString } from 'ele-admin-pro/es';
import type { EleProTable } from 'ele-admin-pro/es'; import type { EleProTable } from 'ele-admin-pro/es';
import type { Security, SecurityParam } from '@/api/tower/security/model'; import type { Security, SecurityParam } from '@/api/tower/security/model';
import { import { pageSecurity, removeSecurity } from '@/api/tower/security';
pageSecurity,
listSecurity,
removeSecurity
} from '@/api/tower/security';
import SecurityEdit from './components/security-edit.vue'; import SecurityEdit from './components/security-edit.vue';
import SecurityRecord from './components/security-record.vue'; import SecurityRecord from './components/security-record.vue';
@@ -115,16 +111,16 @@
// dataIndex: 'securityId', // dataIndex: 'securityId',
// key: 'securityId' // key: 'securityId'
// }, // },
{
title: '保养编号',
dataIndex: 'securityCode',
key: 'securityCode'
},
{ {
title: '项目名称', title: '项目名称',
dataIndex: 'projectName', dataIndex: 'projectName',
key: 'projectName' key: 'projectName'
}, },
{
title: '保养编号',
dataIndex: 'securityCode',
key: 'securityCode'
},
{ {
title: '所在地', title: '所在地',
dataIndex: 'address', dataIndex: 'address',
@@ -176,6 +172,7 @@
if (searchText.value && searchText.value != '') { if (searchText.value && searchText.value != '') {
where.keywords = searchText.value; where.keywords = searchText.value;
} }
where.type = 1;
return pageSecurity({ ...where }); return pageSecurity({ ...where });
}; };
@@ -240,3 +237,28 @@
name: 'TowerSecurity' name: 'TowerSecurity'
}; };
</script> </script>
<style lang="less" scoped>
.count {
display: flex;
.btn {
margin: 0 12px;
padding: 6px 18px;
background-color: #f3f3f3;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
font-size: 16px;
.divider {
width: 4px;
height: 15px;
border-radius: 8px;
margin-right: 8px;
background-color: #16a90b;
}
.active {
color: #16a90b;
}
}
}
</style>

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,279 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
: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="projectName">
<ProjectSelectModel
:placeholder="`请选择项目`"
v-model:value="form.projectName"
@done="chooseProject"
/>
</a-form-item>
<a-form-item label="问题描述">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入问题描述"
v-model:value="form.problem"
/>
</a-form-item>
<a-form-item label="处理方法">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入问题描述"
v-model:value="form.processingMethod"
/>
</a-form-item>
<a-form-item label="上传图片" name="files">
<ele-image-upload
v-model:value="files"
:item-style="{ width: '90px', height: '90px' }"
:limit="20"
@upload="onUpload"
/>
</a-form-item>
<a-form-item label="是否更换配件" name="menuType">
<a-radio-group
v-model:value="form.replaceAccessories"
@change="onReplaceAccessories"
>
<a-radio :value="1"></a-radio>
<a-radio :value="0"></a-radio>
</a-radio-group>
</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 { FormInstance, Rule, RuleObject } 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';
import type { ItemType } from "ele-admin-pro/es/ele-image-upload/types";
import { uploadFile } from "@/api/system/file";
import { pop } from "echarts/types/src/component/dataZoom/history";
// 是否开启响应式布局
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 files = ref<ItemType[]>([]);
// 表单数据
const { form, resetFields, assignFields } = useFormData<Security>({
securityId: undefined,
type: 2,
problem: '',
processingMethod: '',
files: '',
replaceAccessories: 0,
projectId: undefined,
projectName: undefined,
securityCode: undefined,
address: '',
status: 0,
comments: '',
sortNumber: 100
});
// 表单验证规则
const rules = reactive<Record<string, Rule[]>>({
projectName: [
{
required: true,
message: '请选项项目',
type: 'string',
trigger: 'blur',
validator: async (_rule: RuleObject, value: string) => {
if (value == "") {
return Promise.reject("请输入项目名称");
}
return Promise.resolve();
}
}
],
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 onReplaceAccessories = (e) => {
form.replaceAccessories = e.target.value;
}
// 上传文件
const onUpload = (d: ItemType) => {
uploadFile(<File>d.file)
.then((result) => {
// form.files = result.thumbnail;
files.value.push({
uid: result.id,
url: result.url,
status: "done"
})
message.success('上传成功');
})
.catch((e) => {
message.error(e.message);
});
};
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const categoryForm = {
...form,
files: JSON.stringify(files.value)
};
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) => {
form.projectId = row.projectId;
form.projectName = row.projectName;
form.address = row.projectAddress;
};
watch(
() => props.visible,
(visible) => {
if (visible) {
files.value = [];
if (props.data) {
assignFields({
...props.data
});
if(props.data.replaceAccessories == 1){
form.replaceAccessories = 1
}else {
form.replaceAccessories = 0
}
if (props.data.files) {
const arr = JSON.parse(props.data.files);
arr.map((d, i) => {
files.value.push({
uid: d.uid,
url: d.url,
status: 'done'
});
});
}
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,300 @@
<template>
<div class="ele-body">
<a-card :bordered="false" style="margin-top: 10px">
<!-- 表格 -->
<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-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-input-search
allow-clear
v-model:value="searchText"
placeholder="请输入搜索关键词"
@search="search"
@pressEnter="search"
/>
</a-space>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'files'">
<a-image-preview-group v-if="record.files">
<a-image
:width="70"
v-for="(item, index) in JSON.parse(record.files)"
:key="index"
:src="item.url"
/>
</a-image-preview-group>
</template>
<template v-if="column.key === 'replaceAccessories'">
<a-tag v-if="record.replaceAccessories"></a-tag>
<a-tag v-else></a-tag>
</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>
<!-- 编辑弹窗 -->
<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, 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: 'projectName',
key: 'projectName'
},
{
title: '保养编号',
dataIndex: 'securityCode',
key: 'securityCode'
},
{
title: '问题描述',
dataIndex: 'problem',
key: 'problem'
},
{
title: '处理方法',
dataIndex: 'processingMethod',
key: 'processingMethod'
},
{
title: '图片附件',
dataIndex: 'files',
width: 300,
key: 'files'
},
{
title: '所在地',
dataIndex: 'address',
key: 'address',
showSorterTooltip: false,
ellipsis: true
},
{
title: '是否更换配件',
dataIndex: 'replaceAccessories',
align: 'center',
key: 'replaceAccessories'
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
align: 'center',
width: 100
},
{
title: '创建时间',
dataIndex: 'createTime',
align: 'center',
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;
}
where.type = 2;
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>
<style lang="less" scoped>
.count {
display: flex;
.btn {
margin: 0 12px;
padding: 6px 18px;
background-color: #f3f3f3;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
font-size: 16px;
.divider {
width: 4px;
height: 15px;
border-radius: 8px;
margin-right: 8px;
background-color: #16a90b;
}
.active {
color: #16a90b;
}
}
}
</style>