初始化
This commit is contained in:
63
src/pages-system/area/components/area-tree-item.vue
Normal file
63
src/pages-system/area/components/area-tree-item.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<view class="mb-16rpx overflow-hidden rounded-12rpx bg-white shadow-sm">
|
||||
<view
|
||||
class="flex items-center justify-between p-24rpx"
|
||||
@click="handleToggle"
|
||||
>
|
||||
<view class="flex items-center">
|
||||
<!-- 展开/收起图标 -->
|
||||
<view class="mr-16rpx w-40rpx">
|
||||
<wd-icon
|
||||
v-if="hasChildren"
|
||||
:name="expanded ? 'arrow-down' : 'arrow-right'"
|
||||
size="16px"
|
||||
color="#999"
|
||||
/>
|
||||
</view>
|
||||
<!-- 地区信息 -->
|
||||
<view class="text-28rpx text-[#333]">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
</view>
|
||||
<!-- 编码 -->
|
||||
<view class="text-24rpx text-[#999]">
|
||||
编码:{{ item.id }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 子节点 -->
|
||||
<view v-if="expanded && hasChildren" class="border-t border-[#f5f5f5] pl-56rpx">
|
||||
<AreaTreeItem
|
||||
v-for="child in item.children"
|
||||
:key="child.id"
|
||||
:item="child"
|
||||
:level="level + 1"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { Area } from '@/api/system/area'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
item: Area
|
||||
level?: number
|
||||
}>(), {
|
||||
level: 0,
|
||||
})
|
||||
|
||||
const expanded = ref(false)
|
||||
const hasChildren = computed(() => props.item.children && props.item.children.length > 0)
|
||||
|
||||
/** 切换展开/收起 */
|
||||
function handleToggle() {
|
||||
if (hasChildren.value) {
|
||||
expanded.value = !expanded.value
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
74
src/pages-system/area/components/ip-query-form.vue
Normal file
74
src/pages-system/area/components/ip-query-form.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<wd-popup v-model="visible" position="bottom" closable safe-area-inset-bottom>
|
||||
<view class="p-32rpx">
|
||||
<view class="mb-24rpx text-32rpx font-semibold">
|
||||
IP 查询
|
||||
</view>
|
||||
<wd-input
|
||||
v-model="ipAddress"
|
||||
label="IP 地址"
|
||||
label-width="160rpx"
|
||||
placeholder="请输入 IP 地址"
|
||||
clearable
|
||||
/>
|
||||
<wd-input
|
||||
v-model="ipResult"
|
||||
label="地址"
|
||||
label-width="160rpx"
|
||||
placeholder="展示查询 IP 结果"
|
||||
readonly
|
||||
class="mt-24rpx"
|
||||
/>
|
||||
<wd-button type="primary" block class="mt-32rpx" @click="handleQueryIp">
|
||||
查询
|
||||
</wd-button>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import { getAreaByIp } from '@/api/system/area'
|
||||
import { isIp } from '@/utils/validator'
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: boolean]
|
||||
}>()
|
||||
|
||||
const visible = ref(false)
|
||||
const ipAddress = ref('')
|
||||
const ipResult = ref('')
|
||||
|
||||
watch(() => props.modelValue, (val) => {
|
||||
visible.value = val
|
||||
if (val) {
|
||||
ipAddress.value = ''
|
||||
ipResult.value = ''
|
||||
}
|
||||
})
|
||||
|
||||
watch(visible, (val) => {
|
||||
emit('update:modelValue', val)
|
||||
})
|
||||
|
||||
/** 查询 IP */
|
||||
async function handleQueryIp() {
|
||||
if (!ipAddress.value) {
|
||||
uni.showToast({ title: '请输入 IP 地址', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!isIp(ipAddress.value)) {
|
||||
uni.showToast({ title: '请输入正确的 IP 地址', icon: 'none' })
|
||||
return
|
||||
}
|
||||
ipResult.value = await getAreaByIp(ipAddress.value)
|
||||
uni.showToast({ title: '查询成功', icon: 'success' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
91
src/pages-system/area/index.vue
Normal file
91
src/pages-system/area/index.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<view class="yd-page-container">
|
||||
<!-- 顶部导航栏 -->
|
||||
<wd-navbar
|
||||
title="地区管理"
|
||||
left-arrow placeholder safe-area-inset-top fixed
|
||||
@click-left="handleBack"
|
||||
/>
|
||||
|
||||
<!-- 地区树列表 -->
|
||||
<view class="p-24rpx">
|
||||
<!-- 加载中 -->
|
||||
<view v-if="loading" class="py-100rpx text-center">
|
||||
<wd-loading />
|
||||
</view>
|
||||
<!-- 地区树 -->
|
||||
<view v-else-if="areaList.length > 0">
|
||||
<AreaTreeItem
|
||||
v-for="item in areaList"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
/>
|
||||
</view>
|
||||
<!-- 空状态 -->
|
||||
<view v-else class="py-100rpx text-center">
|
||||
<wd-status-tip image="content" tip="暂无地区数据" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索按钮 -->
|
||||
<wd-fab
|
||||
position="right-bottom"
|
||||
type="primary"
|
||||
:expandable="false"
|
||||
icon="search"
|
||||
@click="handleOpenIpQuery"
|
||||
/>
|
||||
|
||||
<!-- IP 查询弹窗 -->
|
||||
<IpQueryForm v-model="showIpQuery" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { Area } from '@/api/system/area'
|
||||
import { ref } from 'vue'
|
||||
import { getAreaTree } from '@/api/system/area'
|
||||
import { navigateBackPlus } from '@/utils'
|
||||
import AreaTreeItem from './components/area-tree-item.vue'
|
||||
import IpQueryForm from './components/ip-query-form.vue'
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '',
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const areaList = ref<Area[]>([])
|
||||
|
||||
const showIpQuery = ref(false) // 是否显示 IP 查询弹窗
|
||||
|
||||
/** 获取地区树 */
|
||||
async function getList() {
|
||||
loading.value = true
|
||||
try {
|
||||
areaList.value = await getAreaTree()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开 IP 查询弹窗 */
|
||||
function handleOpenIpQuery() {
|
||||
showIpQuery.value = true
|
||||
}
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
navigateBackPlus()
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
Reference in New Issue
Block a user