daoyou_manage_web/src/views/manage/hotel/index.vue
2024-12-25 10:37:14 +08:00

441 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="120px">
<el-form-item label="住宿商家名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入住宿商家名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="发布状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择发布状态" style="width: 200px" @keyup.enter="handleQuery">
<el-option label="未发布" value="0" />
<el-option label="已发布" value="1" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button v-hasPermi="['manage:hotel:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button v-hasPermi="['manage:hotel:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"
>删除</el-button
>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="hotelList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column v-if="true" label="ID" align="center" prop="id" />
<el-table-column label="住宿商家名称" align="center" prop="name" />
<el-table-column label="商家图片" align="center" prop="imageUrl" />
<el-table-column label="商家地理位置" align="center" prop="address" />
<el-table-column label="联系方式" align="center" prop="number" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<el-tag v-if="scope.row.status == 1" type="primary">已发布</el-tag>
<el-tag v-if="scope.row.status == 0" type="info">未发布</el-tag>
</template>
</el-table-column>
<el-table-column label="更新者" align="center" prop="updateByName" />
<el-table-column label="更新时间" align="center" prop="updateTime" width="180" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip v-if="scope.row.status != 1" content="编辑" placement="top">
<el-button v-hasPermi="['manage:hotel:edit']" link type="primary" @click="handleUpdate(scope.row)">编辑</el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.status != 1" content="删除" placement="top">
<el-button v-hasPermi="['manage:hotel:remove']" link type="primary" @click="handleDelete(scope.row)">删除</el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.status == 0" content="发布" placement="top">
<el-button v-hasPermi="['manage:hotel:remove']" link type="primary" @click="handleEdit(scope.row)">发布</el-button>
</el-tooltip>
<el-tooltip v-if="scope.row.status == 1" content="撤销发布" placement="top">
<el-button v-hasPermi="['manage:hotel:remove']" link type="primary" @click="handleEdit(scope.row)">撤销发布</el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card>
<!-- 添加或修改住宿对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="50%" append-to-body>
<el-form ref="hotelFormRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="住宿商家名称" prop="name">
<el-input v-model="form.name" placeholder="请输入住宿商家名称" style="width: 300px" />
</el-form-item>
<el-form-item label="联系电话" prop="number">
<el-input
v-model="form.number"
placeholder="请输入联系电话"
style="width: 300px"
oninput="if(value.length>20)value=value.slice(0,20)"
οnkeyup="value=value.replace(/[^\d\-\d]/g,'')"
/>
</el-form-item>
<el-form-item label="商家图片">
<el-input v-model="imgUrlList.imgUrl" placeholder="请输入商家图片" style="width: 300px" />
</el-form-item>
<!-- <el-form-item label="商家地理位置" prop="address">-->
<!-- <el-input v-model="form.address" placeholder="请输入商家地理位置" style="width: 300px" />-->
<!-- </el-form-item>-->
<el-row>
<el-col :span="24">
<el-form-item label="地理位置" prop="address">
<!-- <el-input v-model="searchLocation" type="text" placeholder="请输入地址信息" clearable /> -->
<el-input
v-model="form.address"
placeholder="请输入地址信息"
class="input-with-select"
style="width: 300px"
@keyup.enter="debouncedSearch(form.address)"
>
<template #append>
<el-button :icon="Search" @click="debouncedSearch(form.address)" />
</template>
</el-input>
</el-form-item>
<tlbs-map
v-if="dialog.visible"
ref="map"
style="margin-left: 80px"
api-key="
6XFBZ-SAVLT-JGIX2-VOLMK-6S2H3-XUBGO"
:center="center"
:zoom="zoom"
:control="control"
@click="onClick"
>
<tlbs-multi-marker :geometries="geometries" :styles="styles" :options="options" />
</tlbs-map>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Hotel" lang="ts">
import { listHotel, getHotel, delHotel, addHotel, updateHotel } from '@/api/manage/hotel';
import { HotelVO, HotelQuery, HotelForm } from '@/api/manage/hotel/types';
import { Search } from '@element-plus/icons-vue';
import { debounce } from '@/utils/debounce';
import { jsonp } from 'vue-jsonp';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const hotelList = ref<HotelVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const idsname = ref([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const hotelFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: HotelForm = {
id: undefined,
name: undefined,
number: undefined,
status: undefined,
longitude: undefined,
latitude: undefined,
province: undefined,
city: undefined,
county: undefined,
address: undefined,
imgUrlList: undefined
};
const data = reactive<PageData<HotelForm, HotelQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
name: undefined,
number: undefined,
status: undefined,
longitude: undefined,
latitude: undefined,
province: undefined,
city: undefined,
county: undefined,
params: {}
},
rules: {
id: [{ required: true, message: '用户ID不能为空', trigger: 'blur' }],
name: [{ required: true, message: '住宿商家名称不能为空', trigger: 'blur' }],
number: [{ required: true, message: '联系方式不能为空', trigger: 'blur' }],
status: [{ required: true, message: '状态 0-未发布 1-审核中 2-发布不能为空', trigger: 'change' }],
longitude: [{ required: true, message: '经度不能为空', trigger: 'blur' }],
latitude: [{ required: true, message: '纬度不能为空', trigger: 'blur' }],
province: [{ required: true, message: '省份编码不能为空', trigger: 'blur' }],
city: [{ required: true, message: '市级编码不能为空', trigger: 'blur' }],
county: [{ required: true, message: '区县编码不能为空', trigger: 'blur' }],
address: [{ required: true, message: '地理位置不能为空', trigger: 'blur' }]
}
});
const imgUrlList = reactive({
imgUrl: '',
fileId: '22'
});
const { queryParams, form, rules } = toRefs(data);
/** 查询住宿列表 */
const getList = async () => {
loading.value = true;
const res = await listHotel(queryParams.value);
hotelList.value = res.rows;
total.value = res.total;
loading.value = false;
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
hotelFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: HotelVO[]) => {
console.log(selection, 'selection');
// multiple.value = !selection.length;
let result = selection.some((item: any) => {
return item.status === 1;
});
multiple.value = result;
console.log(result, multiple.value);
ids.value = selection.map((item) => item.id);
idsname.value = selection.map((item) => item.name);
single.value = selection.length != 1;
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = '添加住宿';
geometries.value = [{ styleId: 'marker', position: { lat: form.value.latitude || 39.145902, lng: form.value.longitude || 117.17546 } }];
center.value = { lat: form.value.latitude || 39.145902, lng: form.value.longitude || 117.17546 };
};
/** 修改按钮操作 */
const handleUpdate = async (row?: HotelVO) => {
reset();
const _id = row?.id || ids.value[0];
const res = await getHotel(_id);
res.data.regionCode = res.data?.county || res.data?.city || res.data?.province;
Object.assign(form.value, res.data);
center.value = { lat: row.latitude, lng: row.longitude };
geometries.value = [{ styleId: 'marker', position: { lat: row.latitude, lng: row.longitude } }];
dialog.visible = true;
dialog.title = '修改住宿';
};
/** 提交按钮 */
const submitForm = () => {
hotelFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
form.value.status = 0;
form.value.imgUrlList = imgUrlList;
if (form.value.id) {
await updateHotel(form.value).finally(() => (buttonLoading.value = false));
} else {
await addHotel(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row?: HotelVO) => {
console.log(ids.value);
const _ids = row?.id || ids.value;
const _idname = row?.name || idsname.value;
await proxy?.$modal.confirm('是否确认删除商家名称为"' + _idname + '"的数据项?').finally(() => (loading.value = false));
await delHotel(_ids);
proxy?.$modal.msgSuccess('删除成功');
await getList();
};
// 撤销发布/发布
const handleEdit = async (row?: HotelVO) => {
const _id = row?.id || ids.value[0];
const res = await getHotel(_id);
Object.assign(form.value, res.data);
const _idname = row?.name;
const text = row?.status != 1 ? '是否确认发布商家名称为' : '是否撤销发布商家名称为';
const messages = row?.status != 1 ? '发布成功' : '撤销成功';
await proxy?.$modal.confirm(text + _idname + '"的数据项?').finally(() => (loading.value = false));
const template = {
...row,
status: row.status == 1 ? 0 : 1,
regionCode: row?.county || row?.city || row?.province
};
// form.value.name = row.name
await updateHotel(template).finally(() => (buttonLoading.value = false));
proxy?.$modal.msgSuccess(messages);
await getList();
};
//输入位置,搜索位置
const performSearch = async (text) => {
// 这里简单模拟延迟,模拟真实的异步请求耗时
// await new Promise((resolve) => setTimeout(resolve, 500));
// 假设这里根据输入的文本去查找匹配的数据,实际中替换成真实逻辑
if (text === '') {
console.log('搜索内容为空');
} else {
console.log('搜索内容为:', text);
// 调用腾讯地图API进行搜索并展示在地图上
// geocoder(text).then((result) => {
// console.log(result);
// });
jsonp(`https://apis.map.qq.com/ws/geocoder/v1/?address=${text}&key=${'6XFBZ-SAVLT-JGIX2-VOLMK-6S2H3-XUBGO'}&output=jsonp`, {}).then((data) => {
console.log(data);
if (data.status == 0) {
center.value.lat = data.result.location.lat;
center.value.lng = data.result.location.lng;
form.value.address =
data.result.address_components.province +
data.result.address_components.city +
data.result.address_components.district +
data.result.address_components.street +
data.result.address_components.street_number;
form.value.latitude = data.result.location.lat;
form.value.longitude = data.result.location.lng;
// form.value.region = data.result.ad_info.adcode; //行政区划编码
form.value.regionCode = data.result.ad_info.adcode; //行政区划编码
// form.value.province = data.result.address_components.province;
// form.value.city = data.result.address_components.city;
geometries.value = [{ styleId: 'marker', position: { lat: data.result.location.lat, lng: data.result.location.lng } }];
} else if (data.status == 348) {
//地址信息不明确,需弹出提示
proxy?.$modal.msgError('未查询到地点,请补充详细地址信息');
return;
}
});
}
};
// 点击地点,获取经纬度及地址信息
const onClick = (e: any) => {
jsonp(
`https://apis.map.qq.com/ws/geocoder/v1/?key=${'6XFBZ-SAVLT-JGIX2-VOLMK-6S2H3-XUBGO'}&location=${e.latLng.lat},${e.latLng.lng}&output=jsonp`,
{}
).then((data) => {
if (data.status == 0) {
if (form.value) {
form.value.address =
data.result.address_component.province +
data.result.address_component.city +
data.result.address_component.district +
data.result.address_component.street +
data.result.address_component.street_number;
form.value.latitude = data.result.location.lat;
form.value.longitude = data.result.location.lng;
// form.value.province = data.result.address_component.province;
// form.value.city = data.result.address_component.city;
// form.value.region = data.result.ad_info.adcode; //行政区划编码
form.value.regionCode = data.result.ad_info.adcode; //行政区划编码
center.value.lat = data.result.location.lat;
center.value.lng = data.result.location.lng;
searchLocation.value = data.result.formatted_addresses.recommend;
geometries.value = [{ styleId: 'marker', position: { lat: data.result.location.lat, lng: data.result.location.lng } }];
}
}
});
};
const debouncedSearch = debounce(performSearch, 300); // 创建防抖后的搜索函数延迟设为300毫秒
const map = ref(null);
const center = ref({ lat: form.value.latitude || 39.145902, lng: form.value.longitude || 117.17546 });
const zoom = ref(20);
const control = reactive({
scale: {},
zoom: {
position: 'topLeft'
}
});
const searchLocation = ref(''); // 搜索地点
const geometries = ref([{ styleId: 'marker', position: { lat: form.value.latitude || 39.145902, lng: form.value.longitude || 117.17546 } }]);
const styles = reactive({
marker: {
width: 20,
height: 30,
anchor: { x: 10, y: 30 }
}
});
const options = reactive({
minZoom: 5,
maxZoom: 20
});
// 监听输入框内容变化,调用防抖后的搜索函数
watch(searchLocation, (newValue) => {
// debouncedSearch(newValue);
});
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'manage/hotel/export',
{
...queryParams.value
},
`hotel_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getList();
});
</script>
<style scoped></style>