10 changed files with 746 additions and 292 deletions
@ -0,0 +1,248 @@ |
|||
<template> |
|||
<el-dialog |
|||
v-model="dialogVisible" |
|||
:title="t('mapPickerTitle')" |
|||
width="800px" |
|||
:before-close="handleClose" |
|||
> |
|||
|
|||
<div class="map-container" id="container"></div> |
|||
<div class="address-search"> |
|||
<el-select |
|||
v-model="province" |
|||
:placeholder="t('provincePlaceholder')" |
|||
@change="handleProvinceChange" |
|||
> |
|||
<el-option |
|||
v-for="item in provinceList" |
|||
:key="item.id" |
|||
:label="item.name" |
|||
:value="item.id" |
|||
/> |
|||
</el-select> |
|||
<el-select |
|||
v-model="city" |
|||
:placeholder="t('cityPlaceholder')" |
|||
@change="handleCityChange" |
|||
:disabled="!province" |
|||
> |
|||
<el-option |
|||
v-for="item in cityList" |
|||
:key="item.id" |
|||
:label="item.name" |
|||
:value="item.id" |
|||
/> |
|||
</el-select> |
|||
<el-select |
|||
v-model="district" |
|||
:placeholder="t('districtPlaceholder')" |
|||
@change="handleDistrictChange" |
|||
:disabled="!province || !city" |
|||
> |
|||
<el-option |
|||
v-for="item in districtList" |
|||
:key="item.id" |
|||
:label="item.name" |
|||
:value="item.id" |
|||
/> |
|||
</el-select> |
|||
<el-input |
|||
v-model="detailAddress" |
|||
:placeholder="t('detailAddressPlaceholder')" |
|||
/> |
|||
<el-button type="primary" @click="handleAddressSearch">{{ t('search') }}</el-button> |
|||
</div> |
|||
<template #footer> |
|||
<span class="dialog-footer"> |
|||
<el-button @click="dialogVisible = false">{{ t('cancel') }}</el-button> |
|||
<el-button type="primary" @click="handleConfirm"> |
|||
{{ t('confirm') }} |
|||
</el-button> |
|||
</span> |
|||
</template> |
|||
</el-dialog> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
import { ref, onMounted, watch } from 'vue' |
|||
import { getAreaListByPid, getAreaByCode } from '@/app/api/sys' |
|||
import { createMarker, latLngToAddress, addressToLatLng } from '@/utils/qqmap' |
|||
import { t } from '@/lang' |
|||
|
|||
const props = defineProps({ |
|||
modelValue: { |
|||
type: Object, |
|||
default: () => ({ |
|||
lat: null, |
|||
lng: null, |
|||
address: '', |
|||
}), |
|||
}, |
|||
}) |
|||
|
|||
const emit = defineEmits(['update:modelValue', 'confirm']) |
|||
|
|||
const dialogVisible = ref(false) |
|||
|
|||
const handleClose = (done: () => void) => { |
|||
done() |
|||
} |
|||
|
|||
const handleConfirm = () => { |
|||
emit('confirm', { |
|||
lat: props.modelValue.lat, |
|||
lng: props.modelValue.lng, |
|||
address: detailAddress.value |
|||
}) |
|||
dialogVisible.value = false |
|||
} |
|||
|
|||
// 地图相关 |
|||
let map: any |
|||
let marker: any |
|||
const mapKey = ref('') |
|||
|
|||
// 区域选择 |
|||
const province = ref('') |
|||
const city = ref('') |
|||
const district = ref('') |
|||
const detailAddress = ref('') |
|||
const provinceList = ref<any[]>([]) |
|||
const cityList = ref<any[]>([]) |
|||
const districtList = ref<any[]>([]) |
|||
|
|||
// 初始化地图 |
|||
onMounted(() => { |
|||
const mapScript = document.createElement('script') |
|||
mapKey.value = 'IZQBZ-3UHEU-WTCVD-2464U-I5N4V-ZFFU3' |
|||
mapScript.type = 'text/javascript' |
|||
mapScript.src = |
|||
'https://map.qq.com/api/gljs?libraries=tools,service&v=1.exp&key=IZQBZ-3UHEU-WTCVD-2464U-I5N4V-ZFFU3' |
|||
document.body.appendChild(mapScript) |
|||
mapScript.onload = () => { |
|||
setTimeout(() => { |
|||
initMap() |
|||
}, 500) |
|||
} |
|||
|
|||
// 初始化省份列表 |
|||
getAreaListByPid(0).then((res) => { |
|||
provinceList.value = res.data |
|||
}) |
|||
}) |
|||
|
|||
const initMap = () => { |
|||
const TMap = (window as any).TMap |
|||
const center = new TMap.LatLng(39.90403, 116.407526) |
|||
map = new TMap.Map('container', { |
|||
center, |
|||
zoom: 12, |
|||
}) |
|||
|
|||
marker = createMarker(map) |
|||
|
|||
map.on('click', (evt: any) => { |
|||
map.setCenter(evt.latLng) |
|||
marker.updateGeometries({ |
|||
id: 'center', |
|||
position: evt.latLng, |
|||
}) |
|||
emit('update:modelValue', { |
|||
lat: evt.latLng.lat, |
|||
lng: evt.latLng.lng, |
|||
address: detailAddress.value, |
|||
}) |
|||
}) |
|||
} |
|||
|
|||
// 区域选择处理 |
|||
const handleProvinceChange = (val: string) => { |
|||
getAreaListByPid(val).then((res) => { |
|||
cityList.value = res.data |
|||
city.value = '' |
|||
district.value = '' |
|||
}) |
|||
} |
|||
|
|||
const handleCityChange = (val: string) => { |
|||
getAreaListByPid(val).then((res) => { |
|||
districtList.value = res.data |
|||
district.value = '' |
|||
}) |
|||
} |
|||
|
|||
const handleDistrictChange = (val: string) => { |
|||
// TODO: 处理区县选择 |
|||
} |
|||
|
|||
// 地址搜索 |
|||
const handleAddressSearch = () => { |
|||
const address = [ |
|||
province.value |
|||
? provinceList.value.find((p) => p.id === province.value)?.name |
|||
: '', |
|||
city.value ? cityList.value.find((c) => c.id === city.value)?.name : '', |
|||
district.value |
|||
? districtList.value.find((d) => d.id === district.value)?.name |
|||
: '', |
|||
detailAddress.value, |
|||
].join('') |
|||
|
|||
addressToLatLng({ mapKey: mapKey.value, address }).then( |
|||
({ message, result }) => { |
|||
if (message == 'Success' || message == 'query ok') { |
|||
const latLng = new (window as any).TMap.LatLng( |
|||
result.location.lat, |
|||
result.location.lng |
|||
) |
|||
map.setCenter(latLng) |
|||
marker.updateGeometries({ |
|||
id: 'center', |
|||
position: latLng, |
|||
}) |
|||
emit('update:modelValue', { |
|||
lat: result.location.lat, |
|||
lng: result.location.lng, |
|||
address: detailAddress.value, |
|||
}) |
|||
} |
|||
} |
|||
) |
|||
} |
|||
|
|||
// 回显处理 |
|||
watch( |
|||
() => props.modelValue, |
|||
(newVal) => { |
|||
if (newVal.lat && newVal.lng) { |
|||
const latLng = new (window as any).TMap.LatLng(newVal.lat, newVal.lng) |
|||
map?.setCenter(latLng) |
|||
marker?.updateGeometries({ |
|||
id: 'center', |
|||
position: latLng, |
|||
}) |
|||
} |
|||
}, |
|||
{ immediate: true } |
|||
) |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.map-picker { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 16px; |
|||
} |
|||
|
|||
.map-container { |
|||
width: 100%; |
|||
height: 400px; |
|||
border: 1px solid #dcdfe6; |
|||
border-radius: 4px; |
|||
} |
|||
|
|||
.address-search { |
|||
display: flex; |
|||
gap: 8px; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,37 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | Niucloud-admin 企业快速开发的多应用管理平台 |
|||
// +---------------------------------------------------------------------- |
|||
// | 官方网址:https://www.niucloud.com |
|||
// +---------------------------------------------------------------------- |
|||
// | niucloud团队 版权所有 开源版本可自由商用 |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: Niucloud Team |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
use think\facade\Route; |
|||
|
|||
use app\adminapi\middleware\AdminCheckRole; |
|||
use app\adminapi\middleware\AdminCheckToken; |
|||
use app\adminapi\middleware\AdminLog; |
|||
// USER_CODE_BEGIN -- campus |
|||
|
|||
Route::group('campus', function () { |
|||
|
|||
//校区列表 |
|||
Route::get('campus', 'campus.Campus/lists'); |
|||
//校区详情 |
|||
Route::get('campus/:id', 'campus.Campus/info'); |
|||
//添加校区 |
|||
Route::post('campus', 'campus.Campus/add'); |
|||
//编辑校区 |
|||
Route::put('campus/:id', 'campus.Campus/edit'); |
|||
//删除校区 |
|||
Route::delete('campus/:id', 'campus.Campus/del'); |
|||
|
|||
})->middleware([ |
|||
AdminCheckToken::class, |
|||
AdminCheckRole::class, |
|||
AdminLog::class |
|||
]); |
|||
// USER_CODE_END -- campus |
|||
@ -0,0 +1,94 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | Niucloud-admin 企业快速开发的多应用管理平台 |
|||
// +---------------------------------------------------------------------- |
|||
// | 官方网址:https://www.niucloud.com |
|||
// +---------------------------------------------------------------------- |
|||
// | niucloud团队 版权所有 开源版本可自由商用 |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: Niucloud Team |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace app\model\campus; |
|||
|
|||
use core\base\BaseModel; |
|||
use think\model\concern\SoftDelete; |
|||
use think\model\relation\HasMany; |
|||
use think\model\relation\HasOne; |
|||
|
|||
/** |
|||
* 校区模型 |
|||
* Class Campus |
|||
* @package app\model\campus |
|||
*/ |
|||
class Campus extends BaseModel |
|||
{ |
|||
|
|||
use SoftDelete; |
|||
|
|||
/** |
|||
* 数据表主键 |
|||
* @var string |
|||
*/ |
|||
protected $pk = 'id'; |
|||
|
|||
/** |
|||
* 模型名称 |
|||
* @var string |
|||
*/ |
|||
protected $name = 'campus'; |
|||
|
|||
/** |
|||
* 定义软删除标记字段. |
|||
* @var string |
|||
*/ |
|||
protected $deleteTime = 'delete_time'; |
|||
|
|||
/** |
|||
* 定义软删除字段的默认值. |
|||
* @var int |
|||
*/ |
|||
protected $defaultSoftDelete = 0; |
|||
|
|||
/** |
|||
* 搜索器:校区校区名称 |
|||
* @param $value |
|||
* @param $data |
|||
*/ |
|||
public function searchCampusNameAttr($query, $value, $data) |
|||
{ |
|||
if ($value) { |
|||
$query->where("campus_name", "like", "%".$value."%"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 搜索器:校区校区地址 |
|||
* @param $value |
|||
* @param $data |
|||
*/ |
|||
public function searchCampusAddressAttr($query, $value, $data) |
|||
{ |
|||
if ($value) { |
|||
$query->where("campus_address", $value); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 搜索器:校区校区状态 |
|||
* @param $value |
|||
* @param $data |
|||
*/ |
|||
public function searchCampusStatusAttr($query, $value, $data) |
|||
{ |
|||
if ($value) { |
|||
$query->where("campus_status", $value); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,100 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | Niucloud-admin 企业快速开发的多应用管理平台 |
|||
// +---------------------------------------------------------------------- |
|||
// | 官方网址:https://www.niucloud.com |
|||
// +---------------------------------------------------------------------- |
|||
// | niucloud团队 版权所有 开源版本可自由商用 |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: Niucloud Team |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace app\service\admin\campus; |
|||
|
|||
use app\model\campus\Campus; |
|||
|
|||
use core\base\BaseAdminService; |
|||
|
|||
|
|||
/** |
|||
* 校区服务层 |
|||
* Class CampusService |
|||
* @package app\service\admin\campus |
|||
*/ |
|||
class CampusService extends BaseAdminService |
|||
{ |
|||
public function __construct() |
|||
{ |
|||
parent::__construct(); |
|||
$this->model = new Campus(); |
|||
} |
|||
|
|||
/** |
|||
* 获取校区列表 |
|||
* @param array $where |
|||
* @return array |
|||
*/ |
|||
public function getPage(array $where = []) |
|||
{ |
|||
$field = 'id,campus_name,campus_address,campus_preview_image,campus_coordinates,campus_introduction,campus_status,create_time,update_time,delete_time'; |
|||
$order = ''; |
|||
|
|||
$search_model = $this->model->withSearch(["campus_name","campus_address","campus_status"], $where)->field($field)->order($order); |
|||
$list = $this->pageQuery($search_model); |
|||
return $list; |
|||
} |
|||
|
|||
/** |
|||
* 获取校区信息 |
|||
* @param int $id |
|||
* @return array |
|||
*/ |
|||
public function getInfo(int $id) |
|||
{ |
|||
$field = 'id,campus_name,campus_address,campus_preview_image,campus_coordinates,campus_introduction,campus_status,create_time,update_time,delete_time'; |
|||
|
|||
$info = $this->model->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray(); |
|||
$info['campus_status'] = strval($info['campus_status']); |
|||
return $info; |
|||
} |
|||
|
|||
/** |
|||
* 添加校区 |
|||
* @param array $data |
|||
* @return mixed |
|||
*/ |
|||
public function add(array $data) |
|||
{ |
|||
$res = $this->model->create($data); |
|||
return $res->id; |
|||
|
|||
} |
|||
|
|||
/** |
|||
* 校区编辑 |
|||
* @param int $id |
|||
* @param array $data |
|||
* @return bool |
|||
*/ |
|||
public function edit(int $id, array $data) |
|||
{ |
|||
|
|||
$this->model->where([['id', '=', $id]])->update($data); |
|||
return true; |
|||
} |
|||
|
|||
/** |
|||
* 删除校区 |
|||
* @param int $id |
|||
* @return bool |
|||
*/ |
|||
public function del(int $id) |
|||
{ |
|||
$model = $this->model->where([['id', '=', $id]])->find(); |
|||
$res = $model->delete(); |
|||
return $res; |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | Niucloud-admin 企业快速开发的多应用管理平台 |
|||
// +---------------------------------------------------------------------- |
|||
// | 官方网址:https://www.niucloud.com |
|||
// +---------------------------------------------------------------------- |
|||
// | niucloud团队 版权所有 开源版本可自由商用 |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: Niucloud Team |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace app\validate\campus; |
|||
use core\base\BaseValidate; |
|||
/** |
|||
* 校区验证器 |
|||
* Class Campus |
|||
* @package addon\app\validate\campus |
|||
*/ |
|||
class Campus extends BaseValidate |
|||
{ |
|||
|
|||
protected $rule = [ |
|||
'campus_name' => 'require', |
|||
]; |
|||
|
|||
protected $message = [ |
|||
'campus_name.require' => ['common_validate.require', ['campus_name']], |
|||
]; |
|||
|
|||
protected $scene = [ |
|||
"add" => ['campus_name', 'campus_address', 'campus_preview_image', 'campus_coordinates', 'campus_introduction', 'campus_status'], |
|||
"edit" => ['campus_name', 'campus_address', 'campus_preview_image', 'campus_coordinates', 'campus_introduction', 'campus_status'] |
|||
]; |
|||
|
|||
} |
|||
Loading…
Reference in new issue