Browse Source

修改 bug

master
王泽彦 8 months ago
parent
commit
4c5699cb12
  1. 447
      admin/src/components/TencentMapPicker.vue
  2. 4
      niucloud/app/model/sys/SysMenu.php
  3. 40
      niucloud/app/service/admin/sys/MenuService.php
  4. 46
      niucloud/app/service/admin/sys/SysMenuService.php

447
admin/src/components/TencentMapPicker.vue

@ -53,11 +53,16 @@
:value="item.id"
/>
</el-select>
<el-input v-model="detailAddress" placeholder="输入地区" />
<el-input
v-model="detailAddress"
placeholder="输入详细地址"
@keyup.enter="handleAddressSearch"
@blur="handleAddressBlur"
/>
<el-button
type="primary"
@click="handleAddressSearch"
:disabled="!province || !city || !district"
:disabled="!province || !city"
>{{ t('search') }}</el-button
>
</div>
@ -127,12 +132,140 @@ const provinceList = ref<any[]>([])
const cityList = ref<any[]>([])
const districtList = ref<any[]>([])
// ====================== ======================
const cleanupMap = () => {
if (mapScript) {
try {
document.body.removeChild(mapScript)
} catch (e) {
console.warn('移除地图脚本失败:', e)
}
mapScript = null
}
if (map) {
try {
map.destroy()
} catch (e) {
console.warn('销毁地图实例失败:', e)
}
map = null
}
marker = null
if (resizeTimer) {
clearTimeout(resizeTimer)
resizeTimer = null
}
window.removeEventListener('resize', handleResize)
}
const loadMapSDK = (): Promise<void> => {
return new Promise((resolve, reject) => {
//
if ((window as any).TMap) {
resolve()
return
}
mapKey.value = import.meta.env.VITE_MAP_KEY
mapScript = document.createElement('script')
mapScript.type = 'text/javascript'
mapScript.src = `https://map.qq.com/api/gljs?libraries=tools,service&v=1.exp&key=${mapKey.value}`
mapScript.onload = () => {
if ((window as any).TMap) {
resolve()
} else {
reject(new Error('TMap未定义'))
}
}
mapScript.onerror = (error) => {
reject(error)
}
document.body.appendChild(mapScript)
})
}
const initMap = () => {
const container = document.getElementById('container')
if (!(window as any).TMap || !container) {
throw new Error('地图SDK未加载或容器不存在')
}
const TMap = (window as any).TMap
let center
// 使
if (props.modelValue.lat && props.modelValue.lng) {
center = new TMap.LatLng(props.modelValue.lat, props.modelValue.lng)
} else {
// 使
center = new TMap.LatLng(39.90403, 116.407526)
}
//
map = new TMap.Map('container', {
center,
zoom: 12,
})
//
marker = createMarker(map)
//
if (props.modelValue.lat && props.modelValue.lng) {
marker.updateGeometries({
id: 'center',
position: new TMap.LatLng(props.modelValue.lat, props.modelValue.lng),
})
}
//
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 bindResizeListener = () => {
window.addEventListener('resize', handleResize)
}
const handleResize = () => {
if (resizeTimer) clearTimeout(resizeTimer)
resizeTimer = setTimeout(() => {
if (map && typeof map.setSize === 'function') {
map.setSize()
} else if (map && typeof map.getSize === 'function') {
// 使
map.invalidateSize && map.invalidateSize()
}
}, 300)
}
// ====================== ======================
const handleError = (error: any) => {
console.error('地图组件错误:', error)
ElMessage.error('地图加载失败,请重试')
}
// ====================== ======================
onMounted(() => {
// SDK
const preloadScript = document.createElement('script')
preloadScript.src = `https://map.qq.com/api/gljs?key= ${mapKey.value}`
document.head.appendChild(preloadScript)
mapKey.value = import.meta.env.VITE_MAP_KEY || 'YOUR_API_KEY'
})
// ====================== ======================
@ -211,191 +344,138 @@ const handleConfirm = () => {
dialogVisible.value = false
}
//
//
const parseAndSetAddress = async (address: string) => {
if (!address) return
try {
//
const { provinceName, cityName, districtName, detail } = parseAddress(address)
detailAddress.value = detail
// 1.
// 1.
if (provinceName) {
const provinceItem = provinceList.value.find((p) =>
p.name.includes(provinceName)
)
const provinceItem = provinceList.value.find((p) => {
return p.name.includes(provinceName.replace(/[省市区]/g, '')) ||
p.name === provinceName
})
if (provinceItem) {
province.value = provinceItem.id
// 2.
// 2.
const cityRes = await getAreaListByPid(province.value)
cityList.value = cityRes.data
if (cityName) {
const cityItem = cityList.value.find((c) => c.name.includes(cityName))
const cityItem = cityList.value.find((c) => {
return c.name.includes(cityName.replace(/[市盟州]/g, '')) ||
c.name === cityName
})
if (cityItem) {
city.value = cityItem.id
// 3.
// 3.
const districtRes = await getAreaListByPid(city.value)
districtList.value = districtRes.data
if (districtName) {
const districtItem = districtList.value.find((d) =>
d.name.includes(districtName)
)
const districtItem = districtList.value.find((d) => {
return d.name.includes(districtName.replace(/[区县旗市]/g, '')) ||
d.name === districtName
})
if (districtItem) {
district.value = districtItem.id
}
}
}
}
}
}
} else if (provinceName.includes('市')) {
// ""
const directCity = cityList.value.find((c) => c.name === provinceName)
if (directCity) {
city.value = directCity.id
//
handleAddressSearch()
}
const districtRes = await getAreaListByPid(city.value)
districtList.value = districtRes.data
//
const parseAddress = (address: string) => {
let provinceName = ''
let cityName = ''
let districtName = ''
let detail = address
if (districtName) {
const districtItem = districtList.value.find((d) => {
return d.name.includes(districtName.replace(/[区县旗市]/g, '')) ||
d.name === districtName
})
// //
const provinceMatch = address.match(/^([^省市自治区]+[省市区]|[^市]+市)/)
if (provinceMatch) {
provinceName = provinceMatch[0]
detail = address.slice(provinceName.length)
if (districtItem) {
district.value = districtItem.id
}
//
const cityMatch = detail.match(/^([^市区]+市)/)
if (cityMatch) {
cityName = cityMatch[0]
detail = detail.slice(cityName.length)
}
// /
const districtMatch = detail.match(/^([^区县]+[区县])/)
if (districtMatch) {
districtName = districtMatch[0]
detail = detail.slice(districtName.length)
}
return {
provinceName,
cityName,
districtName,
detail: detail.trim() || '',
}
}
// ====================== ======================
const cleanupMap = () => {
if (mapScript) {
document.body.removeChild(mapScript)
mapScript = null
}
if (map) {
map.destroy()
map = null
//
await nextTick()
if (province.value && city.value) {
handleAddressSearch()
}
marker = null
if (resizeTimer) {
clearTimeout(resizeTimer)
resizeTimer = null
} catch (error) {
console.warn('地址解析失败:', error)
}
window.removeEventListener('resize', handleResize)
}
const loadMapSDK = (): Promise<void> => {
return new Promise((resolve, reject) => {
mapKey.value = import.meta.env.VITE_MAP_KEY
mapScript = document.createElement('script')
mapScript.type = 'text/javascript'
mapScript.src = `https://map.qq.com/api/gljs?libraries=tools ,service&v=1.exp&key=${mapKey.value}`
//
const parseAddress = (address: string) => {
if (!address) return { provinceName: '', cityName: '', districtName: '', detail: '' }
mapScript.onload = () => {
if ((window as any).TMap) {
resolve()
} else {
reject(new Error('TMap未定义'))
}
}
let provinceName = ''
let cityName = ''
let districtName = ''
let detail = address.trim()
mapScript.onerror = (error) => {
reject(error)
}
// ///
const provincePatterns = [
/^([^省市自治区特别行政人民共和]+[省自治区])/, // XX/XX
/^(北京市|上海市|天津市|重庆市)/, //
/^([^市]+特别行政区)/, //
]
document.body.appendChild(mapScript)
})
for (const pattern of provincePatterns) {
const match = detail.match(pattern)
if (match) {
provinceName = match[1]
detail = detail.slice(provinceName.length)
break
}
const initMap = () => {
const container = document.getElementById('container')
if (!(window as any).TMap || !container) {
throw new Error('地图SDK未加载或容器不存在')
}
const TMap = (window as any).TMap
let center
// 使
if (props.modelValue.lat && props.modelValue.lng) {
center = new TMap.LatLng(props.modelValue.lat, props.modelValue.lng)
} else {
// 使
center = new TMap.LatLng(39.90403, 116.407526)
//
if (!provinceName.includes('市')) {
const cityMatch = detail.match(/^([^市区县盟州]+[市盟州])/)
if (cityMatch) {
cityName = cityMatch[1]
detail = detail.slice(cityName.length)
}
//
map = new TMap.Map('container', {
center,
zoom: 12,
})
//
marker = createMarker(map)
//
if (props.modelValue.lat && props.modelValue.lng) {
marker.updateGeometries({
id: 'center',
position: new TMap.LatLng(props.modelValue.lat, props.modelValue.lng),
})
}
//
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 districtMatch = detail.match(/^([^区县旗市]+[区县旗市])/)
if (districtMatch) {
districtName = districtMatch[1]
detail = detail.slice(districtName.length)
}
const bindResizeListener = () => {
window.addEventListener('resize', handleResize)
return {
provinceName: provinceName.trim(),
cityName: cityName.trim(),
districtName: districtName.trim(),
detail: detail.trim(),
}
const handleResize = () => {
if (resizeTimer) clearTimeout(resizeTimer)
resizeTimer = setTimeout(() => {
map?.setSize()
}, 300)
}
// ====================== ======================
const handleProvinceChange = async (val: string) => {
try {
@ -419,32 +499,54 @@ const handleCityChange = async (val: string) => {
}
const handleDistrictChange = (val: string) => {
// TODO:
//
if (val && detailAddress.value.trim()) {
handleAddressSearch()
}
}
//
const handleAddressBlur = () => {
//
if (province.value && city.value && detailAddress.value.trim()) {
handleAddressSearch()
}
}
//
const handleAddressSearch = async () => {
try {
//
if (!province.value || !city.value) {
ElMessage.warning('请选择省市')
return
}
const address = [
province.value
? provinceList.value.find((p) => p.id === province.value)?.name
: '',
city.value ? cityList.value.find((c) => c.id === city.value)?.name : '',
provinceList.value.find((p) => p.id === province.value)?.name || '',
cityList.value.find((c) => c.id === city.value)?.name || '',
district.value
? districtList.value.find((d) => d.id === district.value)?.name
? districtList.value.find((d) => d.id === district.value)?.name || ''
: '',
detailAddress.value,
].join('')
detailAddress.value.trim(),
].filter(Boolean).join('')
const { message, result } = await addressToLatLng({
if (!address.trim()) {
ElMessage.warning('请输入地址')
return
}
const result = await addressToLatLng({
mapKey: mapKey.value,
address,
})
if (message == 'Success' || message == 'query ok') {
//
if (result.status === 0 && result.result) {
//
const latLng = new (window as any).TMap.LatLng(
result.location.lat,
result.location.lng
result.result.location.lat,
result.result.location.lng
)
map.setCenter(latLng)
marker.updateGeometries({
@ -452,22 +554,47 @@ const handleAddressSearch = async () => {
position: latLng,
})
emit('update:modelValue', {
lat: result.location.lat,
lng: result.location.lng,
address: detailAddress.value,
lat: result.result.location.lat,
lng: result.result.location.lng,
address: address, // 使
})
ElMessage.success('地址搜索成功')
} else if (result.status === 112) {
// IP
ElMessage({
type: 'warning',
message: '地址搜索暂不可用(API授权限制),请直接在地图上点击选择位置',
duration: 5000
})
console.warn('腾讯地图API IP授权限制:', result.message)
//
emit('update:modelValue', {
lat: props.modelValue.lat || null,
lng: props.modelValue.lng || null,
address: address, //
})
} else {
//
ElMessage.warning(`搜索失败:${result.message || '未找到对应地址,请检查输入'}`)
}
} catch (error: any) {
console.error('地址搜索异常:', error)
// IP
if (error.status === 112 || (error.message && error.message.includes('IP未被授权'))) {
ElMessage({
type: 'info',
message: '地址搜索暂不可用,请直接在地图上点击选择位置',
duration: 5000
})
} else {
ElMessage.error('搜索失败,请重试')
}
} catch (error) {
handleError(error)
}
}
// ====================== ======================
const handleError = (error: any) => {
console.error('地图组件错误:', error)
ElMessage.error(t('mapLoadFailed'))
dialogVisible.value = false
}
//
watch(

4
niucloud/app/model/sys/SysMenu.php

@ -35,13 +35,13 @@ class SysMenu extends BaseModel
* 模型名称
* @var string
*/
protected $name = 'sys_menus';
protected $name = 'sys_menu';
/**
* 表名(不使用前缀)
* @var string
*/
protected $table = 'sys_menus';
protected $table = 'school_sys_menu';
/**
* 追加字段
* @var array

40
niucloud/app/service/admin/sys/MenuService.php

@ -98,21 +98,47 @@ class MenuService extends BaseAdminService
}
/**
* 菜单删除
* 菜单删除(递归删除子菜单)
* @param string $menu_key
* @return bool
* @throws DbException
*/
public function del(string $menu_key)
{
//查询是否有下级菜单或按钮
$menu = $this->find($menu_key);
if (( new SysMenu() )->where([ [ 'parent_key', '=', $menu_key ] ])->count() > 0)
throw new AdminException('MENU_NOT_ALLOW_DELETE');
// 直接查询菜单是否存在,不依赖缓存
$menu = (new SysMenu())->where('menu_key', $menu_key)->find();
if (!$menu) {
throw new AdminException('菜单不存在');
}
// 递归删除菜单及其子菜单
$this->deleteMenuRecursive($menu_key);
$res = $menu->delete();
// 清除缓存
Cache::tag(self::$cache_tag_name)->clear();
return $res;
return true;
}
/**
* 递归删除菜单及其子菜单
* @param string $menu_key
* @return void
*/
private function deleteMenuRecursive(string $menu_key)
{
// 查找所有子菜单
$childMenus = (new SysMenu())->where('parent_key', $menu_key)->select();
// 递归删除所有子菜单
foreach ($childMenus as $childMenu) {
$this->deleteMenuRecursive($childMenu->menu_key);
}
// 删除当前菜单
$currentMenu = (new SysMenu())->where('menu_key', $menu_key)->find();
if ($currentMenu) {
$currentMenu->delete();
}
}
/**

46
niucloud/app/service/admin/sys/SysMenuService.php

@ -110,7 +110,7 @@ class SysMenuService extends BaseAdminService
}
/**
* 删除菜单
* 删除菜单(递归删除子菜单)
* @param int $id
* @return bool
*/
@ -121,15 +121,45 @@ class SysMenuService extends BaseAdminService
throw new AdminException('菜单不存在');
}
// 检查是否有角色在使用此菜单
$result = Db::query("SELECT COUNT(*) as count FROM role_menu_permissions WHERE menu_id = ?", [$id]);
$roleMenuCount = $result[0]['count'] ?? 0;
if ($roleMenuCount > 0) {
throw new AdminException('该菜单已被角色使用,无法删除');
Db::startTrans();
try {
// 递归删除所有子菜单
$this->deleteMenuRecursive($model->menu_key);
Db::commit();
return true;
} catch (\Exception $e) {
Db::rollback();
throw new AdminException('删除菜单失败:' . $e->getMessage());
}
}
/**
* 递归删除菜单及其子菜单
* @param string $menuKey
* @return void
*/
private function deleteMenuRecursive(string $menuKey)
{
// 获取当前菜单信息
$currentMenu = $this->model->where('menu_key', $menuKey)->find();
if (!$currentMenu) {
return;
}
// 查找所有子菜单
$childMenus = $this->model->where('parent_key', $menuKey)->select();
// 递归删除所有子菜单
foreach ($childMenus as $childMenu) {
$this->deleteMenuRecursive($childMenu->menu_key);
}
// 删除角色菜单权限关联
Db::execute("DELETE FROM role_menu_permissions WHERE menu_id = ?", [$currentMenu->id]);
$res = $model->delete();
return $res !== false;
// 删除当前菜单
$currentMenu->delete();
}
/**

Loading…
Cancel
Save