739 lines
21 KiB
Vue
739 lines
21 KiB
Vue
<template>
|
|
<div class="app-container">
|
|
<!--工具栏-->
|
|
<div class="head-container">
|
|
<div class="filter-container">
|
|
<el-input
|
|
v-model="query.name"
|
|
clearable
|
|
size="small"
|
|
placeholder="请输入飞行器名称"
|
|
style="width: 200px"
|
|
class="filter-item"
|
|
/>
|
|
<el-select
|
|
v-model="query.employeesId"
|
|
placeholder="请选择负责人"
|
|
clearable
|
|
size="small"
|
|
style="width: 200px"
|
|
class="filter-item"
|
|
>
|
|
<el-option
|
|
v-for="employee in employeeOptions"
|
|
:key="employee.id"
|
|
:label="employee.name"
|
|
:value="employee.id"
|
|
/>
|
|
</el-select>
|
|
<el-select
|
|
v-model="query.areaId"
|
|
placeholder="请选择区域"
|
|
size="small"
|
|
style="width: 200px"
|
|
class="filter-item"
|
|
>
|
|
<el-option
|
|
v-for="area in areaOptions"
|
|
:key="area.id"
|
|
:label="area.name"
|
|
:value="area.id"
|
|
/>
|
|
</el-select>
|
|
<el-button
|
|
class="filter-item"
|
|
size="small"
|
|
type="primary"
|
|
icon="el-icon-search"
|
|
@click="crud.toQuery"
|
|
>查询</el-button
|
|
>
|
|
<el-button
|
|
v-permission="permission.add"
|
|
class="filter-item"
|
|
size="small"
|
|
type="success"
|
|
icon="el-icon-plus"
|
|
@click="crud.toAdd"
|
|
v-if="checkPer(permission.add)"
|
|
>新增飞行器</el-button
|
|
>
|
|
</div>
|
|
</div>
|
|
<!--表单渲染-->
|
|
<el-dialog
|
|
append-to-body
|
|
:close-on-click-modal="false"
|
|
:before-close="crud.cancelCU"
|
|
:visible.sync="crud.status.cu > 0"
|
|
:title="crud.status.title"
|
|
width="580px"
|
|
>
|
|
<el-form
|
|
ref="form"
|
|
:inline="true"
|
|
:model="form"
|
|
:rules="rules"
|
|
size="small"
|
|
label-width="100px"
|
|
>
|
|
<el-form-item label="飞行器名称" prop="name">
|
|
<el-input v-model="form.name" style="width: 150px" />
|
|
</el-form-item>
|
|
<el-form-item label="型号" prop="model">
|
|
<el-input v-model="form.model" style="width: 150px" />
|
|
</el-form-item>
|
|
<el-form-item label="类型" prop="useType">
|
|
<el-select v-model="form.useType" style="width: 150px">
|
|
<el-option :value="0" label="载物飞行" />
|
|
<el-option :value="1" label="载人飞行" />
|
|
<el-option :value="2" label="其他" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="区域" prop="areaId">
|
|
<el-select
|
|
v-model="form.areaId"
|
|
@change="changeArea"
|
|
style="width: 150px"
|
|
>
|
|
<el-option
|
|
v-for="area in areaOptions"
|
|
:key="area.id"
|
|
:label="area.name"
|
|
:value="area.id"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="景区" prop="scenicId">
|
|
<el-select v-model="form.scenicId" style="width: 150px">
|
|
<el-option
|
|
v-for="scenic in scenicOptions"
|
|
:key="scenic.id"
|
|
:label="scenic.name"
|
|
:value="scenic.id"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="负责人" prop="employeesId">
|
|
<el-select v-model="form.employeesId" style="width: 150px">
|
|
<el-option
|
|
v-for="employee in employeeOptions"
|
|
:key="employee.id"
|
|
:label="employee.name"
|
|
:value="employee.id"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="备注" prop="remarks" :label-width="'100px'">
|
|
<el-input
|
|
type="textarea"
|
|
:rows="2"
|
|
placeholder="请输入备注"
|
|
v-model="form.remarks"
|
|
style="width: 150px"
|
|
>
|
|
</el-input>
|
|
</el-form-item>
|
|
<el-form-item label="设备图片" prop="deviceImages" style="width: 100%">
|
|
<div class="upload-container">
|
|
<el-upload
|
|
ref="uploadRef"
|
|
action="#"
|
|
list-type="picture-card"
|
|
:show-file-list="false"
|
|
:on-change="handleChange"
|
|
:auto-upload="false"
|
|
accept="image/*"
|
|
:class="{ 'upload-item': true }"
|
|
>
|
|
<div
|
|
style="
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-top: -20px;
|
|
"
|
|
>
|
|
<i class="el-icon-plus" style="font-size: 30px" />
|
|
<span style="margin: 5px 0 -20px; line-height: 20px">上传</span>
|
|
</div>
|
|
</el-upload>
|
|
<div
|
|
v-for="(image, index) in form.deviceImages"
|
|
:key="index"
|
|
class="preview-item"
|
|
>
|
|
<img :src="image.fileFullPath" class="preview-image" alt="" />
|
|
<span class="preview-actions">
|
|
<span
|
|
class="preview-action"
|
|
@click="handlePictureCardPreview(image.fileFullPath)"
|
|
>
|
|
<i class="el-icon-zoom-in" />
|
|
</span>
|
|
<span class="preview-action" @click="handleRemoveImage(index)">
|
|
<i class="el-icon-delete" />
|
|
</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<el-dialog append-to-body :visible.sync="dialogVisible" top="20vh">
|
|
<div>
|
|
<img
|
|
:src="dialogImageUrl"
|
|
style="width: 100%"
|
|
alt="Preview Image"
|
|
/>
|
|
</div>
|
|
</el-dialog>
|
|
</el-form-item>
|
|
</el-form>
|
|
<div slot="footer" class="dialog-footer">
|
|
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
|
<el-button
|
|
:loading="crud.status.cu === 2"
|
|
type="primary"
|
|
@click="crud.submitCU"
|
|
>确认</el-button
|
|
>
|
|
</div>
|
|
</el-dialog>
|
|
<!--表格渲染-->
|
|
<el-table
|
|
ref="table"
|
|
v-loading="crud.loading"
|
|
:data="crud.data"
|
|
row-key="id"
|
|
@select="crud.selectChange"
|
|
@select-all="crud.selectAllChange"
|
|
@selection-change="crud.selectionChangeHandler"
|
|
>
|
|
<el-table-column type="selection" width="55" />
|
|
<el-table-column label="飞行器名称" prop="name" />
|
|
<el-table-column label="型号" prop="model" />
|
|
<el-table-column label="类型" prop="useType">
|
|
<template slot-scope="scope">
|
|
{{
|
|
scope.row.useType === 0
|
|
? "载物行动"
|
|
: scope.row.useType === 1
|
|
? "载人行动"
|
|
: "其他"
|
|
}}
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="负责人" prop="username" />
|
|
<el-table-column label="区域" prop="areaName" />
|
|
<el-table-column label="景区" prop="scenicName" />
|
|
<el-table-column label="创建时间" prop="createTime" width="180" />
|
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
<template slot-scope="scope">
|
|
<el-button size="mini" type="text" @click="handleView(scope.row)">
|
|
查看
|
|
</el-button>
|
|
<el-button
|
|
size="mini"
|
|
type="text"
|
|
@click="crud.toEdit(scope.row)"
|
|
v-permission="permission.edit"
|
|
v-if="checkPer(permission.edit)"
|
|
>
|
|
修改
|
|
</el-button>
|
|
<el-button
|
|
size="mini"
|
|
type="text"
|
|
@click="handleDelete(scope.row)"
|
|
v-permission="permission.del"
|
|
v-if="checkPer(permission.del)"
|
|
>
|
|
删除
|
|
</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
<!--分页-->
|
|
<div class="pagination-container">
|
|
<el-pagination
|
|
:background="true"
|
|
:current-page.sync="page.page"
|
|
:page-size.sync="page.size"
|
|
:layout="'total, sizes, prev, pager, next, jumper'"
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
:total="page.total"
|
|
@size-change="crud.sizeChangeHandler"
|
|
@current-change="crud.pageChangeHandler"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import CRUD, { presenter, header, form, crud } from "@crud/crud";
|
|
import udOperation from "@crud/UD.operation";
|
|
import crudAircraft from "@/api/aircraft";
|
|
import { allAreas } from "@/api/system/area";
|
|
import { getList, getDetail } from "@/api/system/pilot";
|
|
import { allScenic } from "@/api/system/scenic";
|
|
import { upload } from "@/utils/upload";
|
|
import { mapGetters } from "vuex";
|
|
import rrOperation from "@crud/RR.operation";
|
|
// crud交由presenter持有
|
|
const defaultForm = {
|
|
id: 1,
|
|
name: null,
|
|
brand: null,
|
|
model: null,
|
|
useType: 0,
|
|
areaId: null,
|
|
scenicId: null,
|
|
employeesId: null,
|
|
deviceImages: [],
|
|
remark: "",
|
|
createTime: null,
|
|
};
|
|
export default {
|
|
name: "aircraft",
|
|
components: { udOperation },
|
|
cruds() {
|
|
return CRUD({
|
|
title: "飞行器",
|
|
url: "aerocraftAdminApi/aircraft/device/page",
|
|
crudMethod: {
|
|
...crudAircraft,
|
|
getList: crudAircraft.getDevicePage,
|
|
},
|
|
optShow: {
|
|
search: true,
|
|
},
|
|
queryOnPresenterCreated: true, // 启用初始化时自动查询
|
|
page: {
|
|
// 配置分页参数名称
|
|
page: "current",
|
|
size: "size",
|
|
},
|
|
// 配置查询参数
|
|
query: {
|
|
employeesId: undefined,
|
|
areaId: undefined,
|
|
name: undefined,
|
|
},
|
|
initData: (crud, data) => {
|
|
crud.page.total = data.total;
|
|
// 确保每条记录的id是字符串类型
|
|
const records = data.records || [];
|
|
crud.data = records.map(item => ({
|
|
...item,
|
|
id: item.id ? item.id.toString() : ''
|
|
}));
|
|
// username字段需要通过getList()获取
|
|
crud.data.forEach((item) => {
|
|
if (item.employeesId) {
|
|
getList().then((res) => {
|
|
res.content.forEach((employee) => {
|
|
if (employee.id === item.employeesId) {
|
|
item.username = employee.username;
|
|
}
|
|
});
|
|
});
|
|
} else {
|
|
item.username = "未知";
|
|
}
|
|
});
|
|
},
|
|
});
|
|
},
|
|
mixins: [presenter(), header(), form(defaultForm), crud()],
|
|
data() {
|
|
return {
|
|
dialogVisible: false,
|
|
dialogImageUrl: "",
|
|
uploadDisabled: false,
|
|
// ini
|
|
page: {
|
|
page: 1,
|
|
size: 10,
|
|
total: 5,
|
|
},
|
|
areaOptions: [],
|
|
scenicOptions: [],
|
|
employeeOptions: [],
|
|
// 默认数据
|
|
query: {
|
|
name: "",
|
|
employeesId: undefined,
|
|
areaId: undefined,
|
|
current: 1,
|
|
size: 10,
|
|
},
|
|
defaultData: [],
|
|
permission: {
|
|
add: ["admin", "aircraft:add"],
|
|
edit: ["admin", "aircraft:edit"],
|
|
del: ["admin", "aircraft:del"],
|
|
},
|
|
rules: {
|
|
name: [
|
|
{ required: true, message: "请输入飞行器名称", trigger: "blur" },
|
|
],
|
|
brand: [{ required: true, message: "品牌不能为空", trigger: "blur" }],
|
|
model: [{ required: true, message: "请输入型号", trigger: "blur" }],
|
|
useType: [{ required: true, message: "请选择类型", trigger: "change" }],
|
|
areaId: [{ required: true, message: "请选择区域", trigger: "change" }],
|
|
scenicId: [
|
|
{ required: true, message: "请选择景区", trigger: "change" },
|
|
],
|
|
employeesId: [
|
|
{ required: true, message: "请选择负责人", trigger: "change" },
|
|
],
|
|
deviceImages: [
|
|
{
|
|
required: true,
|
|
message: "请上传设备图片",
|
|
trigger: "change",
|
|
type: "array",
|
|
},
|
|
],
|
|
},
|
|
};
|
|
},
|
|
computed: {
|
|
...mapGetters(["imagesUploadApi", "baseApi"]),
|
|
},
|
|
created() {
|
|
// 获取区域列表
|
|
this.getAreas();
|
|
// 获取飞行员列表
|
|
this.getEmployees();
|
|
},
|
|
methods: {
|
|
// 获取区域列表
|
|
async getAreas() {
|
|
try {
|
|
const res = await allAreas();
|
|
if (res) {
|
|
this.areaOptions = res.map((area) => ({
|
|
id: area.id,
|
|
name: area.name,
|
|
}));
|
|
}
|
|
} catch (error) {
|
|
this.$message.error("获取区域列表失败");
|
|
console.error("获取区域列表失败:", error);
|
|
this.areaOptions = [];
|
|
}
|
|
},
|
|
|
|
// 切换区域时获取景区列表
|
|
changeArea(value) {
|
|
// 清空景区选择
|
|
this.query.scenicId = undefined;
|
|
// 获取对应区域的景区列表
|
|
allScenic({ areaId: value }).then((res) => {
|
|
if (res) {
|
|
this.scenicOptions = (res || []).map((scenic) => ({
|
|
id: scenic.id,
|
|
name: scenic.name,
|
|
}));
|
|
} else {
|
|
this.scenicOptions = [];
|
|
}
|
|
});
|
|
},
|
|
|
|
// 获取负责人列表
|
|
async getEmployees() {
|
|
try {
|
|
const res = await getList();
|
|
if (res) {
|
|
this.employeeOptions = res.content.map((employee) => ({
|
|
id: employee.id,
|
|
name: employee.username,
|
|
}));
|
|
}
|
|
} catch (error) {
|
|
this.$message.error("获取负责人列表失败");
|
|
console.error("获取负责人列表失败:", error);
|
|
this.employeeOptions = [];
|
|
}
|
|
},
|
|
|
|
[CRUD.HOOK.beforeToEdit](crud, form) {
|
|
// 获取对应的景区列表
|
|
if (form.areaId) {
|
|
allScenic({ areaId: form.areaId }).then((res) => {
|
|
if (res) {
|
|
this.scenicOptions = (res || []).map((scenic) => ({
|
|
id: scenic.id,
|
|
name: scenic.name,
|
|
}));
|
|
} else {
|
|
this.scenicOptions = [];
|
|
}
|
|
});
|
|
} else {
|
|
this.scenicOptions = [];
|
|
}
|
|
// 获取对应的负责人列表
|
|
if (form.employeesId) {
|
|
getList().then((res) => {
|
|
if (res) {
|
|
this.employeeOptions = res.content.map((employee) => ({
|
|
id: employee.id,
|
|
name: employee.username,
|
|
}));
|
|
} else {
|
|
this.employeeOptions = [];
|
|
}
|
|
});
|
|
} else {
|
|
this.employeeOptions = [];
|
|
}
|
|
},
|
|
|
|
[CRUD.HOOK.beforeSubmit]() {
|
|
// 设置brand的值与name相同
|
|
this.form.brand = this.form.name;
|
|
return true;
|
|
},
|
|
|
|
[CRUD.HOOK.beforeRefresh]() {
|
|
// 设置分页参数
|
|
this.crud.params.current = this.page.page;
|
|
this.crud.params.size = this.page.size;
|
|
|
|
// 设置查询参数
|
|
this.crud.params.employeesId = this.query.employeesId;
|
|
this.crud.params.areaId = this.query.areaId;
|
|
this.crud.params.name = this.query.name;
|
|
return true;
|
|
},
|
|
|
|
[CRUD.HOOK.afterRefresh]() {
|
|
// 更新分页数据
|
|
const response = this.crud.data;
|
|
|
|
if (response) {
|
|
// 确保有total字段
|
|
if (response.total !== undefined) {
|
|
this.page.total = Number(response.total);
|
|
}
|
|
// 确保records数组存在且是数组类型
|
|
if (response && Array.isArray(response)) {
|
|
// 创建一个Promise数组来存储所有的详情请求
|
|
const detailPromises = response.map(item => {
|
|
if (!item || !item.id) return Promise.resolve(null);
|
|
const stringId = item.id.toString();
|
|
return crudAircraft.getDeviceDetail(stringId).then(res => {
|
|
if (!res) return null;
|
|
return {
|
|
...item,
|
|
...res,
|
|
id: stringId
|
|
};
|
|
});
|
|
});
|
|
|
|
// 等待所有详情请求完成后再更新数据
|
|
Promise.all(detailPromises).then(detailedData => {
|
|
// 过滤掉空值并确保每个项都有一个有效的字符串id
|
|
this.crud.data = detailedData.filter(item => item && item.id && typeof item.id === 'string');
|
|
}).catch(error => {
|
|
console.error('获取设备详情失败:', error);
|
|
this.$message.error('获取设备详情失败');
|
|
});
|
|
} else {
|
|
// 如果没有records字段或不是数组,设置为空数组避免报错
|
|
this.crud.data = [];
|
|
}
|
|
} else {
|
|
// 如果响应为空,也设置为空数组
|
|
this.crud.data = [];
|
|
this.page.total = 0;
|
|
}
|
|
},
|
|
|
|
handleView(row) {
|
|
const tempRow = { areaName: row.areaName, scenicName: row.scenicName, username: row.username };
|
|
this.$router.push({
|
|
path: "/system/aircraftDetail/index",
|
|
query: {
|
|
id: row.id,
|
|
data: JSON.stringify(tempRow)
|
|
},
|
|
});
|
|
},
|
|
|
|
// 预览图片
|
|
handlePictureCardPreview(url) {
|
|
this.dialogImageUrl = url;
|
|
this.dialogVisible = true;
|
|
},
|
|
|
|
// 删除图片
|
|
handleRemoveImage(index) {
|
|
this.form.deviceImages.splice(index, 1);
|
|
},
|
|
|
|
// 触发图片上传
|
|
handleChange(file, uploadFiles) {
|
|
if (file.raw.type.includes("image")) {
|
|
this.loading = true;
|
|
try {
|
|
upload(this.imagesUploadApi, file.raw).then((res) => {
|
|
if (res.status === 200) {
|
|
const data = res.data;
|
|
// 构造deviceImages对象
|
|
const deviceImage = {
|
|
fileFullPath: data.fileFullPath,
|
|
fileSize: file.raw.size,
|
|
fileType: file.raw.type.split("/")[1],
|
|
newFileName: data.newFileName,
|
|
sourceFileName: file.raw.name,
|
|
};
|
|
// 将新图像添加到现有数组
|
|
if (!this.form.deviceImages) {
|
|
this.form.deviceImages = [];
|
|
}
|
|
this.form.deviceImages.push(deviceImage);
|
|
this.$message.success("图片上传成功!");
|
|
this.loading = false;
|
|
} else {
|
|
this.$message.error("图片上传失败!");
|
|
this.loading = false;
|
|
return;
|
|
}
|
|
});
|
|
} catch (error) {
|
|
this.loading = false;
|
|
this.$message.error("图片上传失败!");
|
|
}
|
|
} else {
|
|
this.$confirm("图片的格式不正确,请重新选择!", "提示", {
|
|
confirmButtonText: "确定",
|
|
type: "warning",
|
|
});
|
|
uploadFiles.pop();
|
|
}
|
|
},
|
|
|
|
handleDelete(row) {
|
|
this.$confirm(`此操作将永久删除飞行器"${row.name}",是否继续?`, "提示", {
|
|
confirmButtonText: "确定",
|
|
cancelButtonText: "取消",
|
|
type: "warning",
|
|
})
|
|
.then(() => {
|
|
crudAircraft.del(row.id).then(() => {
|
|
this.$notify({
|
|
title: "成功",
|
|
type: "success",
|
|
message: "删除成功!",
|
|
});
|
|
this.crud.refresh();
|
|
});
|
|
})
|
|
.catch(() => {
|
|
this.$message({
|
|
type: "info",
|
|
message: "已取消删除",
|
|
});
|
|
});
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
|
.upload-container {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.upload-item {
|
|
::v-deep .el-upload--picture-card {
|
|
width: 110px !important;
|
|
height: 110px !important;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin: 0;
|
|
}
|
|
}
|
|
|
|
.preview-item {
|
|
position: relative;
|
|
width: 110px;
|
|
height: 110px;
|
|
border: 1px solid #c0ccda;
|
|
border-radius: 6px;
|
|
|
|
.preview-image {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.preview-actions {
|
|
position: absolute;
|
|
width: 100%;
|
|
height: 100%;
|
|
left: 0;
|
|
top: 0;
|
|
cursor: default;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
opacity: 0;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
border-radius: 6px;
|
|
transition: opacity 0.3s;
|
|
|
|
&:hover {
|
|
opacity: 1;
|
|
}
|
|
|
|
.preview-action {
|
|
color: #fff;
|
|
font-size: 18px;
|
|
margin: 0 7px;
|
|
cursor: pointer;
|
|
|
|
&:hover {
|
|
opacity: 0.8;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// .pagination-container {
|
|
// position: fixed;
|
|
// bottom: 0;
|
|
// right: 0;
|
|
// left: 200px;
|
|
// height: 150px;
|
|
// background: white;
|
|
// padding: 10px;
|
|
// text-align: left;
|
|
// box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.08);
|
|
// }
|
|
// .el-pagination {
|
|
// padding: 2px 25px;
|
|
// }
|
|
.app-container {
|
|
padding-bottom: 60px;
|
|
}
|
|
::v-deep .el-input-number .el-input__inner {
|
|
text-align: left;
|
|
}
|
|
::v-deep .vue-treeselect__control,
|
|
::v-deep .vue-treeselect__placeholder,
|
|
::v-deep .vue-treeselect__single-value {
|
|
height: 30px;
|
|
line-height: 30px;
|
|
}
|
|
</style>
|